To Do

Overview

Summary

Photography has been a hobby of mine for most of my life, and I found a particular niche in abstract photography, specifically multi-exposure images. This background inspired me to find mathematical ways to analyze my photo library as a whole, with a special focus on color trends and affinities.

Business Application

The processes used in this project have a business application within a mobile app. By evaluating a user’s camera roll, the app could discern favorite colors and suggest products that match that color profile.

Data Collection Method

Flickr API

  • Worked backwards from the present to separate generic images (downloads, memes, screenshots, videos) from photos (robust EXIF data)
  • Collected 4500 usable flickr IDs (more IDs meant a larger date range to sample from)
    • (Python script was used to randomly select from the mega-list)
  • Selected IDs had their EXIF data collected (again) and jpg downloaded at “XL” size
  • EXIF data was saved to its own CSV

Python

  • Each photo was mathematically divided into 9 subimages, to allow for full-photo trends to be compared against center-image trends
    • (All calculations were done on each photo 10 times, once for the full photo, and once for each sub-image. In retrospect, much of the sub-image processing was redundant and could have been gathered via subsetting the full-image matrix)
  • Gamma adjustment (RGB linearization)
  • Conversion to HSL for “readable” values (colorsys library)
  • Segmentation (“posterization”) (pymeanshift library)

Note:

Shout out to my buddy Phil! He was a great resource for feedback and encouragement as I formulated my processing script, but also donated runtime on his computer and processed 250 images used in this dataset.

PyMeanShift/Segmentation

A photograph with 4000 pixels may have 4000 different color values represented. I wanted to “clump” pixels with similar colors in the same area of an image into a single color value. PyMeanShift accomplishes this by taking in the image and three numerical variables: spatial radius, range radius, and minimum density. These refer to maximum color difference, maximum placement difference, and minimum “clump” size, respectively.

https://ieeexplore.ieee.org/document/1000236

Datapoints Collected

EXIF

FlickrID - unique identifier for each photo

DateTimeOriginal/CreateDate/ModifyDate - attempted to capture whether the images were edited on the phone (unsuccessful)

Software - iOS version or mobile app used for photo capture

LensInfo/ LensModel - data on which phone lens capture the photo

JFIFVersion - compression marker applied by some 3rd party apps. Disappears when image is edited in native iOS photos app.

ISO - light sensitivity setting

ExposureTime - in seconds (fractions)

FNumber - aperture

FocalLength- Fixed to LensInfo/LensModel

FocalLengthIn35mmFormat - iOS interpretation of zoom level

BrightnessValue - Auto-generated brightness value

SubjectArea -  Coordinate values generated by iOS (not directly relevant to this project, but captured for future use)

Image Data

A python class was used to gather image data as attributes, then dumped to a csv with vars(). 

All relevant attributes/variables described below

using_id - Flickr ID

img_width - in pixels

img_height - in pixels

do_img_at - timestamp for evaluating processing time 

sub_img - 0 for whole image, 1 for top-left, 2 for middle-left, 5 for center, etc.

full_id - concat of flickrID and sub_image to form unique identifier.

RGB Overview Statistics

(r/g/b)_min - (3 columns) Minimum red/green/blue channel value in the whole image

(r/g/b)_max - (3 columns) Maximum red/green/blue channel value

(r/g/b)_mean - (3 columns) Average red/green/blue channel value

(r/g/b)_mode - (3 columns) count of common red/green/blue channel value (forgot to capture its value :facepalm: (in my attempt to capture the value, I neglected to reset the index of the pandas dataSeries))

center_rgb - (tuple) R/G/B value of the pixel mathematically in the center of the image

Next segment of columns captured from segmented/posterized image

post_num_regions - number of color “clumps” after processing

post_top_hsl - (tuple) most common pixel value

post_top_count - quantity of most common pixel value

post_(2-6)_hsl - (5 columns)(tuples) next most common pixel values, in descending order of frequency

post_(2-6)_count - (5 columns) counts for their respective common pixel values

center_hsl - (tuple) HSL value of the pixel mathematically in the center of the image

Hue color banding was done by subjective eyeball measurement

All hues: red, orange, yellow, green, cyan, blue, purple, magenta

full_(hue)_count - count of all pixels that fell within the hue band, regardless of saturation and lightness

visib_(hue)_count - count of pixels in the hue band deemed as “visibly [hue]” (saturation over 40%, lightness between 20% and 75%)

vivid_(hue)_count - count of pixels in the hue band deemed as “vividly [hue]” (saturation over 70%, lightness between 30% and 70%)

Saturation Statistics

sat_min_val - lowest saturation value in image

sat_25_val - 25% quartile value

sat_50_val - median saturation

sat_75_val - 75% quartile value

sat_max_val - most saturation

HSL Mean Values

hue_mean_val - average hue value (not incredibly meaningful on a looping spectrum)

sat_mean_val - average saturation value

light_mean_val - average brightness

Lightness Statistics

light_max_val - brightest value

light_max_count - quantity of pixels within 1.5% (literal) of the max lightness value

light_min_val - darkest value

light_min_count - quantity of pixels within 1.5% (literal) of he minimum lightness value (darkest)

light_25_value - 25% quartile value

light_50_value - median brightness

light_75_value - 75% quartile value

gen_bright_count - quantity of pixels with over 85% lightness

gen_dark_count - quantity of pixels with under 15% brightness

common_hsl_(1-4)_val - (4 columns)(tuple) four most common HSL values

common_hsl_(1-4)_count - (4 columns) quantities of the four most common HSL values

Data Collation

Due to collecting image processing data on multiple computers, multiple files were created for exif and image data – partly by design and party due to occasional read/write conflicts on shared files. All records were gathered into Excel and checked for duplicates before exporting as CSVs.

Hypotheses

H1 - Hue vs Date

\(Ho:\) There is no correlation between time of year and color values

\(Ha:\) Warm color values are more prominent between May and September

H2 - Lightness vs Time

\(Ho:\) There is no correlation between time of day and lightness values

\(Ha:\) Lightness values are higher between 6 am and 6pm

H3 - Saturation vs Subject

\(Ho:\) There is no correlation between saturation and being a picture of my cat

\(Ha:\) Low saturation values are increasingly common over time, especially in central sub-images

H4 - Vividness vs Image Type

\(Ho:\) Vivid ratio (percentage of vivid pixels) is uniformly distributed among all Software types

\(Ha:\) Vivid ratio is consistently highest in Slow Shutter Cam photos without JFIF values

R

Preparing the Data

Imports


library(tidyverse)
library(plotly)
library(reshape2)
exif <- read_csv("capstone_exif.csv")
img_data <- read_csv("capstone_img_data.csv")

# spec(exif)
# spec(img_data)

Cleaning

Pre Import:
Sub-image data for main images (0) was bugged in the first hours of image processing. This was fixed in Excel during the data collation stage.

EXIF:

  • Fix column headers
  • Drop columns: date_time_original, modify_date, lens_info, f_number, focal_length
  • NA values - JFIF <- 0,  subject_area <- >depends on data type< “0 0 0 0”
names(exif) <- gsub("([a-z0-9])([A-Z])", "\\1_\\2", names(exif))
names(exif) <- names(exif) %>% tolower()

exif_tidy <- select(exif, -c(date_time_original, modify_date, lens_info, fnumber, focal_length))
exif_tidy <- replace_na(exif_tidy, list(subject_area = "0 0 0 0", jfifversion = 0))

Img_data:

  • Drop columns: flickr, img_loc, the_image, crop_coords, r_mode, b_mode, g_mode, img_height, img_width, do_img_at
  • Na values - post_2-6_hsl <- (-1,-1,-1)
imgsd_tidy <- select(img_data, -c(flickr, img_loc, the_image, img_width, img_height, crop_coords, do_img_at, r_mode, b_mode, g_mode))

imgsd_tidy <- replace_na(imgsd_tidy, list(
  post_2_hsl = "(-1, -1, -1)",
  post_3_hsl = "(-1, -1, -1)",
  post_4_hsl = "(-1, -1, -1)",
  post_5_hsl = "(-1, -1, -1)",
  post_6_hsl = "(-1, -1, -1)"
  )
  )

Basic Feature Engineering

EXIF

  • Split date_time_original to year, month-day, time columns
  • Date column uses a placeholder year so month-to-month comparisons are consistent

exif_tidy <- exif_tidy %>% separate(create_date, into = c('full_date', 'time'), sep = " ", remove = TRUE) %>% separate(full_date, into = c('year', 'month', 'day'), sep = ":", remove = FALSE) 

exif_tidy$date <- as.Date(paste("1881", exif_tidy$month, exif_tidy$day, sep = "-"), format ="%Y-%m-%d")

Img_data

  • Count by flickr id

    • Add count(flickr_id) to EXIF
    • Flag counts under 10
    • Flag counts under 6 (ie: subimage 5 not available)
    • Output list of all flickrIDs with less than 10 records for additional (future) processing

subimg_qty <- imgsd_tidy %>% count(using_id)

To my (happy) surprise, only 5 images have less than 10 results and only 2 have less than 6. In the interest of time, I’m noting these IDs by hand and simply removing them from my working data


good_ids <- subimg_qty[subimg_qty$n >=6, "using_id"]

imgsd_tidy <- imgsd_tidy %>% filter(using_id %in% good_ids$using_id)

exif_tidy <- exif_tidy %>% filter(flickr_id %in% good_ids$using_id)
  • Split pixel lists/tuples for 3-d mapping (solely the domain of part 1)

  • (Re)set count values as integers

  • Total pixels = full_(hue)_count(s) (dimension data incorrect for first 1400 records)

  • Generate ratio columns


# after working with this data for 12 hours, I find out there are hidden characeters in my data that were not displaying in Excel.

imgsd_tidy$post_top_count <- as.integer(imgsd_tidy$post_top_count)
imgsd_tidy$post_2_count <- as.integer(imgsd_tidy$post_2_count)
imgsd_tidy$post_3_count <- as.integer(imgsd_tidy$post_3_count)
imgsd_tidy$post_4_count <- as.integer(imgsd_tidy$post_4_count)
imgsd_tidy$post_5_count <- as.integer(imgsd_tidy$post_5_count)
imgsd_tidy$post_6_count <- as.integer(imgsd_tidy$post_6_count)

imgsd_tidy$common_hsl_1_count <- as.integer(imgsd_tidy$common_hsl_1_count)
imgsd_tidy$common_hsl_2_count <- as.integer(imgsd_tidy$common_hsl_2_count)
imgsd_tidy$common_hsl_3_count <- as.integer(imgsd_tidy$common_hsl_3_count)
imgsd_tidy$common_hsl_4_count <- as.integer(imgsd_tidy$common_hsl_4_count)

imgsd_tidy$full_red_count <- as.integer(imgsd_tidy$full_red_count)
imgsd_tidy$full_orange_count <- as.integer(imgsd_tidy$full_orange_count)
imgsd_tidy$full_yellow_count <- as.integer(imgsd_tidy$full_yellow_count)
imgsd_tidy$full_green_count <- as.integer(imgsd_tidy$full_green_count)
imgsd_tidy$full_cyan_count <- as.integer(imgsd_tidy$full_cyan_count)
imgsd_tidy$full_blue_count <- as.integer(imgsd_tidy$full_blue_count)
imgsd_tidy$full_purple_count <- as.integer(imgsd_tidy$full_purple_count)
imgsd_tidy$full_mag_count <- as.integer(imgsd_tidy$full_mag_count)
imgsd_tidy$visib_red_count <- as.integer(imgsd_tidy$visib_red_count)
imgsd_tidy$visib_orange_count <- as.integer(imgsd_tidy$visib_orange_count)
imgsd_tidy$visib_yellow_count <- as.integer(imgsd_tidy$visib_yellow_count)
imgsd_tidy$visib_green_count <- as.integer(imgsd_tidy$visib_green_count)
imgsd_tidy$visib_cyan_count <- as.integer(imgsd_tidy$visib_cyan_count)
imgsd_tidy$visib_blue_count <- as.integer(imgsd_tidy$visib_blue_count)
imgsd_tidy$visib_purple_count <- as.integer(imgsd_tidy$visib_purple_count)
imgsd_tidy$visib_mag_count <- as.integer(imgsd_tidy$visib_mag_count)
imgsd_tidy$vivid_red_count <- as.integer(imgsd_tidy$vivid_red_count)
imgsd_tidy$vivid_orange_count <- as.integer(imgsd_tidy$vivid_orange_count)
imgsd_tidy$vivid_yellow_count <- as.integer(imgsd_tidy$vivid_yellow_count)
imgsd_tidy$vivid_green_count <- as.integer(imgsd_tidy$vivid_green_count)
imgsd_tidy$vivid_cyan_count <- as.integer(imgsd_tidy$vivid_cyan_count)
imgsd_tidy$vivid_blue_count <- as.integer(imgsd_tidy$vivid_blue_count)
imgsd_tidy$vivid_purple_count <- as.integer(imgsd_tidy$vivid_purple_count)
imgsd_tidy$vivid_mag_count <- as.integer(imgsd_tidy$vivid_mag_count)
imgsd_tidy$vivid_count <- as.integer(imgsd_tidy$vivid_count)

imgsd_tidy$gen_bright_count <- as.integer(imgsd_tidy$gen_bright_count)
imgsd_tidy$gen_dark_count <- as.integer(imgsd_tidy$gen_dark_count)

imgsd_tidy <- imgsd_tidy %>% mutate(total_pixels = full_red_count +
                                      full_orange_count +
                                      full_yellow_count +
                                      full_green_count +
                                      full_cyan_count +
                                      full_blue_count + 
                                      full_purple_count +
                                      full_mag_count)

# more type conversions
imgsd_tidy$total_pixels <- as.integer(imgsd_tidy$total_pixels)
imgsd_tidy$light_mean_val <- as.numeric(imgsd_tidy$light_mean_val)
imgsd_tidy$post_num_regions <- as.integer(imgsd_tidy$post_num_regions)
imgsd_tidy$r_mean <- as.numeric(imgsd_tidy$r_mean)
imgsd_tidy$g_mean <- as.numeric(imgsd_tidy$g_mean)
imgsd_tidy$b_mean <- as.numeric(imgsd_tidy$b_mean)


#establishing ratio columns
imgsd_ratio <- imgsd_tidy %>% 
  mutate(ratio_post_top_hsl = post_top_count / total_pixels) %>%
  mutate(ratio_post_2_hsl = post_2_count / total_pixels) %>%
  mutate(ratio_post_3_hsl = post_3_count / total_pixels) %>%
  mutate(ratio_post_4_hsl = post_4_count / total_pixels) %>%
  mutate(ratio_post_5_hsl = post_5_count / total_pixels) %>%
  mutate(ratio_post_6_hsl = post_6_count / total_pixels) %>%
  mutate(ratio_full_red = full_red_count / total_pixels) %>%
  mutate(ratio_full_oragne = full_orange_count / total_pixels) %>%
  mutate(ratio_full_yellow = full_yellow_count / total_pixels) %>%
  mutate(ratio_full_green = full_green_count / total_pixels) %>%  
  mutate(ratio_full_cyan = full_cyan_count / total_pixels) %>%  
  mutate(ratio_full_blue = full_blue_count / total_pixels) %>%  
  mutate(ratio_full_purple = full_purple_count / total_pixels) %>%  
  mutate(ratio_full_mag = full_mag_count / total_pixels) %>%  
  mutate(ratio_full_red = full_red_count / total_pixels) %>%
  mutate(ratio_visib_red = visib_red_count / total_pixels) %>%
  mutate(ratio_visib_oragne = visib_orange_count / total_pixels) %>%
  mutate(ratio_visib_yellow = visib_yellow_count / total_pixels) %>%
  mutate(ratio_visib_green = visib_green_count / total_pixels) %>%  
  mutate(ratio_visib_cyan = visib_cyan_count / total_pixels) %>%  
  mutate(ratio_visib_blue = visib_blue_count / total_pixels) %>%  
  mutate(ratio_visib_purple = visib_purple_count / total_pixels) %>%  
  mutate(ratio_visib_mag = visib_mag_count / total_pixels) %>%
  mutate(ratio_vivid_red = vivid_red_count / total_pixels) %>%
  mutate(ratio_vivid_oragne = vivid_orange_count / total_pixels) %>%
  mutate(ratio_vivid_yellow = vivid_yellow_count / total_pixels) %>%
  mutate(ratio_vivid_green = vivid_green_count / total_pixels) %>%  
  mutate(ratio_vivid_cyan = vivid_cyan_count / total_pixels) %>%  
  mutate(ratio_vivid_blue = vivid_blue_count / total_pixels) %>%  
  mutate(ratio_vivid_purple = vivid_purple_count / total_pixels) %>%  
  mutate(ratio_vivid_mag = vivid_mag_count / total_pixels) %>%
  mutate(ratio_vivid = vivid_count / total_pixels)

Exploration

2D Scatter

  • frequency of most common posterized color vs most common native color

fig <- plot_ly(imgsd_tidy, 
               x= ~post_top_count, 
               y= ~common_hsl_1_count, 
               type = "scatter", mode="markers", size = 2, color = ~sub_img)

fig
  • Light min x light max
fig <- plot_ly(imgsd_tidy, 
               x=~light_min_val, 
               y= ~light_max_val, 
               type = "scatter", mode="markers", size = 2, color = ~sub_img)

fig
  • Gen bright x gen dark

fig <- plot_ly(imgsd_tidy, 
               x=~gen_bright_count, 
               y= ~gen_dark_count, 
               type = "scatter", mode="markers", size = 2, color = ~sub_img)

fig
  • Sat min x sat max

fig <- plot_ly(imgsd_tidy, 
               x=~sat_min_val, 
               y= ~sat_max_val, 
               type = "scatter", mode="markers", size = 2, color = ~sub_img)

fig

1D Histograms

  • Vivid %

Due to a very large number of images that have fewer than 2% vivid pixels, Vivid Ratios are separated out into three segments: 0% vivid pixels, 0-1% vivid pixels, and more than 1% vivid pixels


vivid_ratio <- imgsd_ratio %>% select(ratio_vivid)
vivid_ratio$ratio_vivid <- as.numeric(vivid_ratio$ratio_vivid)

vivid_ratio <- vivid_ratio %>% mutate(
  ratio_rank = case_when(
    ratio_vivid == 0 ~ 0, 
    ratio_vivid > 0 & ratio_vivid < 0.01 ~ 1, 
    ratio_vivid >= 0.01 ~ 2)
  )

rr_counts <- count(vivid_ratio, ratio_rank)

vrr_pie <- plot_ly(rr_counts, labels=~factor(ratio_rank), values= ~n, type='pie')

vrr_pie
# 0 -> No Vivid Pixels
# 1 -> 0-1% vivid pixels
# 2 -> more than 1% vivid pixels

Less vivid pixels than expected! Way less! Only 359 images/subimages had more than 1% of pixels with more than 70% saturation and lightness between 40 and 70%

Now let’s get back to seeing the distribution of those 359 values:


vivid_hist <- vivid_ratio %>% filter(ratio_rank == 2)

fig <- plot_ly(vivid_hist,
               x= ~ratio_vivid,
               type = "histogram")

fig

Future investigation will perform these calculations on the main dataframe in order to access specific image IDs and evaluate the subjective qualities of the top vivid images.

  • Post-num (filter by img segment)

Investigating the distribution of how many posterization “clumps” were in each image


#first setting up filters for more useful faceting going forward
r_fig_filter_0 <- imgsd_ratio %>% filter(imgsd_ratio$sub_img == 0)
r_fig_filter_a <- imgsd_ratio[imgsd_ratio$sub_img %in% c(1, 2, 3), ]
r_fig_filter_b <- imgsd_ratio[imgsd_ratio$sub_img %in% c(4, 5, 6), ]
r_fig_filter_c <- imgsd_ratio[imgsd_ratio$sub_img %in% c(7, 8, 9), ]


fig_0 <- ggplot(r_fig_filter_0,
               aes(x=post_num_regions)) + 
                geom_histogram(binwidth = 5)

fig_a <- ggplot(r_fig_filter_a,
              aes(x=post_num_regions)) + 
              geom_histogram(binwidth = 5) +
              facet_grid (~ sub_img)

fig_b <- ggplot(r_fig_filter_b,
              aes(x=post_num_regions)) + 
              geom_histogram(binwidth = 5) +
              facet_grid (~ sub_img)

fig_c <- ggplot(r_fig_filter_c,
              aes(x=post_num_regions)) + 
              geom_histogram(binwidth = 5) +
              facet_grid (~ sub_img)

fig_0b <- ggplot(r_fig_filter_0,
               aes(x=post_num_regions)) + 
                geom_histogram(binwidth = 50)

fig_0

fig_0b

fig_a

fig_b

fig_c

No huge conclusions to draw about the distribution in sub_image 0, other than the x-scale is shown as roughly 10x (slightly less) than the subimages. Also mildly interesting (also on sub_img 0), the max count for any given bin (at bin-width 5), is a fraction of the scale of the other sub_images. Bumping the sub_img 0 binwidth up to 50 (10x), brings it more in line with the counts in the sub_images, but the long tail on sub_img 0 keeps it from matching exactly.

Otherwise, the only noteworthy observation is the less-intense right skew on sub_images 2, 5, and 8.

These sub-images represent the horizontal middle third of their source images, mimicking the artistic principles of the rule of thirds and putting focal points near the center of the image.

  • Exif brightness

EXIF data, I have not forsaken you.




fig <- ggplot(exif_tidy,
               aes(x=brightness_value)) + 
                geom_histogram(bins=250)

fig

That’s the most normal distribution we’ve seen yet! But still pretty irregular. Let’s see how it compares to the HSL data

  • Mean_lightness

fig_all <- ggplot(imgsd_ratio,
               aes(x=light_mean_val)) + 
                geom_histogram(bins=250)

fig_0 <- ggplot(r_fig_filter_0,
               aes(x=light_mean_val)) + 
                geom_histogram(bins=250)

fig_a <- ggplot(r_fig_filter_a,
              aes(x=light_mean_val)) + 
              geom_histogram(bins=250) +
              facet_grid(~ sub_img)

fig_b <- ggplot(r_fig_filter_b,
              aes(x=light_mean_val)) + 
              geom_histogram(bins=250) +
              facet_grid(~ sub_img)

fig_c <- ggplot(r_fig_filter_c,
              aes(x=light_mean_val)) + 
              geom_histogram(bins=250) +
              facet_grid(~ sub_img)


fig_all

fig_0

fig_a

fig_b

fig_c

Observations: Variation among sub-images is fairly minor, with some some possibility that corner quadrants (1, 3, 7, 9) have wider distributions overall. Lightness values processed for this dataset are centered around 0.625 with a slight left skew. While the EXIF data has a less-normal distribution, it does seem to have a center slightly to the right of its baseline value, but the irregularity of the shape looks more like a right skew than left.

  • (r/g/b) mean

fig <- plot_ly(imgsd_ratio, alpha = .4)
fig <- fig %>% add_histogram(x = ~r_mean)
fig <- fig %>% add_histogram(x = ~g_mean)
fig <- fig %>% add_histogram(x = ~b_mean)
fig <- fig %>% layout(barmode = 'overlay', colorway=c('red', 'green', 'blue'))

fig

Observation: No special information here. As expected, it looks a lot like the mean lightness value distrbution. Almost as if r+g+b = light….

  • Date distribution (with and without year)

fig <- plot_ly(exif_tidy, x = ~date, type = 'histogram')

fig_all <- plot_ly(exif_tidy, x = ~full_date, type = 'histogram')

fig
fig_all

Observation: Even when adjusting for year, the distribution of images is highly irregular. As a month, August is over-represented, and as an individual date, Will and Taylor’s wedding is over-represented.

exif_tidy$time <- strptime(exif_tidy$time, format = "%H:%M%S")

fig <- plot_ly(exif_tidy, x = ~time, type = 'histogram')
fig
Error: C stack usage  15924272 is too close to the limit

Analysis

Final data prep!

Slimming down the EXIF data frame….


#slimming down exif columns....

simple_exif <- exif_tidy %>% select(c(flickr_id, date, month, time, software, jfifversion, brightness_value))

… and adding those EXIF columns to the main dataframe


simple_exif <- simple_exif %>%
  mutate(flickr_id = as.character(flickr_id))

# bringing it all full circle with this variable choice

all_img_data <- imgsd_ratio %>% 
  left_join(simple_exif, by = c("using_id" = "flickr_id"))
Warning: Detected an unexpected many-to-many relationship between `x` and `y`.
#this throws a warning about many-to-many relationships, but this is expected behavior

This space reserved for re-creating sub-frames for ‘faceting’

At last, let’s revisit those hypotheses

H1 - Hue vs Date

\(Ho:\) There is no correlation between time of year and color values

\(Ha:\) Warm color values are more prominent between May and September

Gut Check - Due to the uneven distribution of sample images over time, I don’t have a strong expectation of valid results.


exif_tidy$month <- as.integer(exif_tidy$month)

season_split <- exif_tidy %>% mutate(
  season = case_when(
    month >= 5 & month <= 9 ~ 1, TRUE ~ 0)
  )

season_counts <- table(season_split$season)
season_data <- data.frame(ratio = names(season_counts), count = as.vector(season_counts))

season_data

On the other hand, summer values (622) don’t outrageously outnumber non-summer values.

Now to collect the hue information we will be comparing


all_img_data <- all_img_data %>% 
  mutate(visib_warm = visib_red_count + visib_orange_count + visib_yellow_count + visib_mag_count) %>%
  mutate(visib_cool = visib_green_count + visib_cyan_count + visib_blue_count + visib_purple_count)
  
h1_frame <- all_img_data %>% 
  select(c(using_id, sub_img, total_pixels, visib_warm, visib_cool, date, month)) %>% 
  mutate(warm_ratio = visib_warm / total_pixels) %>% 
  mutate(cool_ratio = visib_cool/total_pixels) %>% 
  mutate(season = case_when(month >= 5 & month <= 9 ~ 1, TRUE ~ 0)) %>%
  mutate(ratio_diff = warm_ratio - cool_ratio)

Testing Time

t.test(warm_ratio ~ season, h1_frame)

    Welch Two Sample t-test

data:  warm_ratio by season
t = 11.179, df = 9078.6, p-value < 2.2e-16
alternative hypothesis: true difference in means between group 0 and group 1 is not equal to 0
95 percent confidence interval:
 0.01619583 0.02308347
sample estimates:
mean in group 0 mean in group 1 
     0.04115762      0.02151798 

Despite my hesitation, the results of the T-test are strongly in favor of rejecting the null hypothesis that there is no difference in visible warmth throughout the year. A high t-value and low p-value are both in support of this conclusion.

H2 - Lightness vs Time

\(Ho:\) There is no correlation between time of day and lightness values

\(Ha:\) Lightness values are higher between 6 am and 6pm


h2_frame <- all_img_data %>% 
  select(c(using_id, sub_img, time, brightness_value, light_mean_val))%>% 
  mutate(daytime = case_when(time >= "06:00:00" & time <= "18:00:00" ~ 1, TRUE ~ 0))

H3 - Saturation vs Subject

\(Ho:\) There is no correlation between saturation and being a picture of my cat

\(Ha:\) Low saturation values are increasingly common over time, especially in central sub-images

H4 - Vividness vs Image Type

\(Ho:\) Vivid ratio (percentage of vivid pixels) is uniformly distributed among all Software types

\(Ha:\) Vivid ratio is consistently highest in Slow Shutter Cam photos without JFIF values

Conclusion

LS0tDQp0aXRsZTogIkRhdGFzZXQgQ2Fwc3RvbmUiDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQojIFRvIERvDQoNCi0gICBGaXggbGlua3MNCg0KLSAgIE1ha2UgYSBnaWYgb2YgcHltZWFuc2hpZnQgc2FtcGxlcyB3aXRoIHZhcmlhYmxlcy9zdGF0cw0KDQotICAgVmlzdWFsaXplIGNvbG9yIHZhbHVlcw0KDQojIE92ZXJ2aWV3DQoNCiMjIFN1bW1hcnkNCg0KUGhvdG9ncmFwaHkgaGFzIGJlZW4gYSBob2JieSBvZiBtaW5lIGZvciBtb3N0IG9mIG15IGxpZmUsIGFuZCBJIGZvdW5kIGEgcGFydGljdWxhciBuaWNoZSBpbiBhYnN0cmFjdCBwaG90b2dyYXBoeSwgc3BlY2lmaWNhbGx5IG11bHRpLWV4cG9zdXJlIGltYWdlcy4gVGhpcyBiYWNrZ3JvdW5kIGluc3BpcmVkIG1lIHRvIGZpbmQgbWF0aGVtYXRpY2FsIHdheXMgdG8gYW5hbHl6ZSBteSBwaG90byBsaWJyYXJ5IGFzIGEgd2hvbGUsIHdpdGggYSBzcGVjaWFsIGZvY3VzIG9uIGNvbG9yIHRyZW5kcyBhbmQgYWZmaW5pdGllcy4NCg0KIyMgQnVzaW5lc3MgQXBwbGljYXRpb24NCg0KVGhlIHByb2Nlc3NlcyB1c2VkIGluIHRoaXMgcHJvamVjdCBoYXZlIGEgYnVzaW5lc3MgYXBwbGljYXRpb24gd2l0aGluIGEgbW9iaWxlIGFwcC4gQnkgZXZhbHVhdGluZyBhIHVzZXIncyBjYW1lcmEgcm9sbCwgdGhlIGFwcCBjb3VsZCBkaXNjZXJuIGZhdm9yaXRlIGNvbG9ycyBhbmQgc3VnZ2VzdCBwcm9kdWN0cyB0aGF0IG1hdGNoIHRoYXQgY29sb3IgcHJvZmlsZS4NCg0KIyMgRGF0YSBDb2xsZWN0aW9uIE1ldGhvZA0KDQojIyMgRmxpY2tyIEFQSQ0KDQotICAgV29ya2VkIGJhY2t3YXJkcyBmcm9tIHRoZSBwcmVzZW50IHRvIHNlcGFyYXRlIGdlbmVyaWMgaW1hZ2VzIChkb3dubG9hZHMsIG1lbWVzLCBzY3JlZW5zaG90cywgdmlkZW9zKSBmcm9tIHBob3RvcyAocm9idXN0IEVYSUYgZGF0YSkNCi0gICBDb2xsZWN0ZWQgNDUwMCB1c2FibGUgZmxpY2tyIElEcyAobW9yZSBJRHMgbWVhbnQgYSBsYXJnZXIgZGF0ZSByYW5nZSB0byBzYW1wbGUgZnJvbSkNCiAgICAtICAgKFB5dGhvbiBzY3JpcHQgd2FzIHVzZWQgdG8gcmFuZG9tbHkgc2VsZWN0IGZyb20gdGhlIG1lZ2EtbGlzdCkNCi0gICBTZWxlY3RlZCBJRHMgaGFkIHRoZWlyIEVYSUYgZGF0YSBjb2xsZWN0ZWQgKGFnYWluKSBhbmQganBnIGRvd25sb2FkZWQgYXQgIlhMIiBzaXplDQotICAgRVhJRiBkYXRhIHdhcyBzYXZlZCB0byBpdHMgb3duIENTVg0KDQojIyMgUHl0aG9uDQoNCi0gICBFYWNoIHBob3RvIHdhcyBtYXRoZW1hdGljYWxseSBkaXZpZGVkIGludG8gOSBzdWJpbWFnZXMsIHRvIGFsbG93IGZvciBmdWxsLXBob3RvIHRyZW5kcyB0byBiZSBjb21wYXJlZCBhZ2FpbnN0IGNlbnRlci1pbWFnZSB0cmVuZHMNCiAgICAtICAgKEFsbCBjYWxjdWxhdGlvbnMgd2VyZSBkb25lIG9uIGVhY2ggcGhvdG8gMTAgdGltZXMsIG9uY2UgZm9yIHRoZSBmdWxsIHBob3RvLCBhbmQgb25jZSBmb3IgZWFjaCBzdWItaW1hZ2UuIEluIHJldHJvc3BlY3QsIG11Y2ggb2YgdGhlIHN1Yi1pbWFnZSBwcm9jZXNzaW5nIHdhcyByZWR1bmRhbnQgYW5kIGNvdWxkIGhhdmUgYmVlbiBnYXRoZXJlZCB2aWEgc3Vic2V0dGluZyB0aGUgZnVsbC1pbWFnZSBtYXRyaXgpDQotICAgR2FtbWEgYWRqdXN0bWVudCAoUkdCIGxpbmVhcml6YXRpb24pDQogICAgLSAgIFtodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9TUkdCI1RyYW5zZmVyX2Z1bmN0aW9uXF8oJTIyZ1xgYW1tYSUyMl0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvU1JHQiNUcmFuc2Zlcl9mdW5jdGlvbl8oJTIyZyU2MGFtbWElMjIpJTdCLnVyaSU3RCkNCi0gICBDb252ZXJzaW9uIHRvIEhTTCBmb3IgInJlYWRhYmxlIiB2YWx1ZXMgKGNvbG9yc3lzIGxpYnJhcnkpDQotICAgU2VnbWVudGF0aW9uICgicG9zdGVyaXphdGlvbiIpIChweW1lYW5zaGlmdCBsaWJyYXJ5KQ0KICAgIC0gICA8aHR0cHM6Ly9naXRodWIuY29tL2ZqZWFuL3B5bWVhbnNoaWZ0Pg0KDQoqKk5vdGUqKjoNCg0KU2hvdXQgb3V0IHRvIG15IGJ1ZGR5IFBoaWwhIEhlIHdhcyBhIGdyZWF0IHJlc291cmNlIGZvciBmZWVkYmFjayBhbmQgZW5jb3VyYWdlbWVudCBhcyBJIGZvcm11bGF0ZWQgbXkgcHJvY2Vzc2luZyBzY3JpcHQsIGJ1dCBhbHNvIGRvbmF0ZWQgcnVudGltZSBvbiBoaXMgY29tcHV0ZXIgYW5kIHByb2Nlc3NlZCAyNTAgaW1hZ2VzIHVzZWQgaW4gdGhpcyBkYXRhc2V0Lg0KDQojIyMjICoqUHlNZWFuU2hpZnQvU2VnbWVudGF0aW9uKioNCg0KQSBwaG90b2dyYXBoIHdpdGggNDAwMCBwaXhlbHMgbWF5IGhhdmUgNDAwMCBkaWZmZXJlbnQgY29sb3IgdmFsdWVzIHJlcHJlc2VudGVkLiBJIHdhbnRlZCB0byAiY2x1bXAiIHBpeGVscyB3aXRoIHNpbWlsYXIgY29sb3JzIGluIHRoZSBzYW1lIGFyZWEgb2YgYW4gaW1hZ2UgaW50byBhIHNpbmdsZSBjb2xvciB2YWx1ZS4gUHlNZWFuU2hpZnQgYWNjb21wbGlzaGVzIHRoaXMgYnkgdGFraW5nIGluIHRoZSBpbWFnZSBhbmQgdGhyZWUgbnVtZXJpY2FsIHZhcmlhYmxlczogc3BhdGlhbCByYWRpdXMsIHJhbmdlIHJhZGl1cywgYW5kIG1pbmltdW0gZGVuc2l0eS4gVGhlc2UgcmVmZXIgdG8gbWF4aW11bSBjb2xvciBkaWZmZXJlbmNlLCBtYXhpbXVtIHBsYWNlbWVudCBkaWZmZXJlbmNlLCBhbmQgbWluaW11bSAiY2x1bXAiIHNpemUsIHJlc3BlY3RpdmVseS4NCg0KPGh0dHBzOi8vaWVlZXhwbG9yZS5pZWVlLm9yZy9kb2N1bWVudC8xMDAwMjM2Pg0KDQojIyBEYXRhcG9pbnRzIENvbGxlY3RlZA0KDQojIyMgRVhJRg0KDQoqKkZsaWNrcklEKiogLSB1bmlxdWUgaWRlbnRpZmllciBmb3IgZWFjaCBwaG90bw0KDQoqKkRhdGVUaW1lT3JpZ2luYWwvQ3JlYXRlRGF0ZS9Nb2RpZnlEYXRlKiogLSBhdHRlbXB0ZWQgdG8gY2FwdHVyZSB3aGV0aGVyIHRoZSBpbWFnZXMgd2VyZSBlZGl0ZWQgb24gdGhlIHBob25lICh1bnN1Y2Nlc3NmdWwpDQoNCioqU29mdHdhcmUqKiAtIGlPUyB2ZXJzaW9uIG9yIG1vYmlsZSBhcHAgdXNlZCBmb3IgcGhvdG8gY2FwdHVyZQ0KDQoqKkxlbnNJbmZvLyBMZW5zTW9kZWwqKiAtIGRhdGEgb24gd2hpY2ggcGhvbmUgbGVucyBjYXB0dXJlIHRoZSBwaG90bw0KDQoqKkpGSUZWZXJzaW9uKiogLSBjb21wcmVzc2lvbiBtYXJrZXIgYXBwbGllZCBieSBzb21lIDNyZCBwYXJ0eSBhcHBzLiBEaXNhcHBlYXJzIHdoZW4gaW1hZ2UgaXMgZWRpdGVkIGluIG5hdGl2ZSBpT1MgcGhvdG9zIGFwcC4NCg0KKipJU08qKiAtIGxpZ2h0IHNlbnNpdGl2aXR5IHNldHRpbmcNCg0KKipFeHBvc3VyZVRpbWUqKiAtIGluIHNlY29uZHMgKGZyYWN0aW9ucykNCg0KKipGTnVtYmVyKiogLSBhcGVydHVyZQ0KDQoqKkZvY2FsTGVuZ3RoKiotIEZpeGVkIHRvIExlbnNJbmZvL0xlbnNNb2RlbA0KDQoqKkZvY2FsTGVuZ3RoSW4zNW1tRm9ybWF0KiogLSBpT1MgaW50ZXJwcmV0YXRpb24gb2Ygem9vbSBsZXZlbA0KDQoqKkJyaWdodG5lc3NWYWx1ZSoqIC0gQXV0by1nZW5lcmF0ZWQgYnJpZ2h0bmVzcyB2YWx1ZQ0KDQoqKlN1YmplY3RBcmVhKiogLcKgIENvb3JkaW5hdGUgdmFsdWVzIGdlbmVyYXRlZCBieSBpT1MgKG5vdCBkaXJlY3RseSByZWxldmFudCB0byB0aGlzIHByb2plY3QsIGJ1dCBjYXB0dXJlZCBmb3IgZnV0dXJlIHVzZSkNCg0KIyMjIEltYWdlIERhdGENCg0KQSBweXRob24gY2xhc3Mgd2FzIHVzZWQgdG8gZ2F0aGVyIGltYWdlIGRhdGEgYXMgYXR0cmlidXRlcywgdGhlbiBkdW1wZWQgdG8gYSBjc3Ygd2l0aCB2YXJzKCkuwqANCg0KQWxsIHJlbGV2YW50IGF0dHJpYnV0ZXMvdmFyaWFibGVzIGRlc2NyaWJlZCBiZWxvdw0KDQoqKnVzaW5nX2lkKiogLSBGbGlja3IgSUQNCg0KKippbWdfd2lkdGgqKiAtIGluIHBpeGVscw0KDQoqKmltZ19oZWlnaHQqKiAtIGluIHBpeGVscw0KDQoqKmRvX2ltZ19hdCoqIC0gdGltZXN0YW1wIGZvciBldmFsdWF0aW5nIHByb2Nlc3NpbmcgdGltZcKgDQoNCioqc3ViX2ltZyoqIC0gMCBmb3Igd2hvbGUgaW1hZ2UsIDEgZm9yIHRvcC1sZWZ0LCAyIGZvciBtaWRkbGUtbGVmdCwgNSBmb3IgY2VudGVyLCBldGMuDQoNCioqZnVsbF9pZCoqIC0gY29uY2F0IG9mIGZsaWNrcklEIGFuZCBzdWJfaW1hZ2UgdG8gZm9ybSB1bmlxdWUgaWRlbnRpZmllci4NCg0KKioqUkdCIE92ZXJ2aWV3IFN0YXRpc3RpY3MqKioNCg0KKiooci9nL2IpXF9taW4qKiAtICooMyBjb2x1bW5zKSogTWluaW11bSByZWQvZ3JlZW4vYmx1ZSBjaGFubmVsIHZhbHVlIGluIHRoZSB3aG9sZSBpbWFnZQ0KDQoqKihyL2cvYilcX21heCoqIC0gKigzIGNvbHVtbnMpKiBNYXhpbXVtIHJlZC9ncmVlbi9ibHVlIGNoYW5uZWwgdmFsdWUNCg0KKiooci9nL2IpXF9tZWFuKiogLSAqKDMgY29sdW1ucykqIEF2ZXJhZ2UgcmVkL2dyZWVuL2JsdWUgY2hhbm5lbCB2YWx1ZQ0KDQoqKihyL2cvYilcX21vZGUqKiAtICooMyBjb2x1bW5zKSogY291bnQgb2YgY29tbW9uIHJlZC9ncmVlbi9ibHVlIGNoYW5uZWwgdmFsdWUgKGZvcmdvdCB0byBjYXB0dXJlIGl0cyB2YWx1ZSA6ZmFjZXBhbG06IChpbiBteSBhdHRlbXB0IHRvIGNhcHR1cmUgdGhlIHZhbHVlLCBJIG5lZ2xlY3RlZCB0byByZXNldCB0aGUgaW5kZXggb2YgdGhlIHBhbmRhcyBkYXRhU2VyaWVzKSkNCg0KKipjZW50ZXJfcmdiKiogLSAqKHR1cGxlKSogUi9HL0IgdmFsdWUgb2YgdGhlIHBpeGVsIG1hdGhlbWF0aWNhbGx5IGluIHRoZSBjZW50ZXIgb2YgdGhlIGltYWdlDQoNCioqKk5leHQgc2VnbWVudCBvZiBjb2x1bW5zIGNhcHR1cmVkIGZyb20gc2VnbWVudGVkL3Bvc3Rlcml6ZWQgaW1hZ2UqKioNCg0KKipwb3N0X251bV9yZWdpb25zKiogLSBudW1iZXIgb2YgY29sb3IgImNsdW1wcyIgYWZ0ZXIgcHJvY2Vzc2luZw0KDQoqKnBvc3RfdG9wX2hzbCoqIC0gKHR1cGxlKSBtb3N0IGNvbW1vbiBwaXhlbCB2YWx1ZQ0KDQoqKnBvc3RfdG9wX2NvdW50KiogLSBxdWFudGl0eSBvZiBtb3N0IGNvbW1vbiBwaXhlbCB2YWx1ZQ0KDQoqKnBvc3RcXygyLTYpXF9oc2wqKiAtICooNSBjb2x1bW5zKSh0dXBsZXMpKiBuZXh0IG1vc3QgY29tbW9uIHBpeGVsIHZhbHVlcywgaW4gZGVzY2VuZGluZyBvcmRlciBvZiBmcmVxdWVuY3kNCg0KKipwb3N0XF8oMi02KVxfY291bnQqKiAtICooNSBjb2x1bW5zKSogY291bnRzIGZvciB0aGVpciByZXNwZWN0aXZlIGNvbW1vbiBwaXhlbCB2YWx1ZXMNCg0KKipjZW50ZXJfaHNsKiogLSAqKHR1cGxlKSogSFNMIHZhbHVlIG9mIHRoZSBwaXhlbCBtYXRoZW1hdGljYWxseSBpbiB0aGUgY2VudGVyIG9mIHRoZSBpbWFnZQ0KDQoqKipIdWUgY29sb3IgYmFuZGluZyB3YXMgZG9uZSBieSBzdWJqZWN0aXZlIGV5ZWJhbGwgbWVhc3VyZW1lbnQqKioNCg0KKioqQWxsIGh1ZXM6IHJlZCwgb3JhbmdlLCB5ZWxsb3csIGdyZWVuLCBjeWFuLCBibHVlLCBwdXJwbGUsIG1hZ2VudGEqKioNCg0KKipmdWxsXF8oaHVlKVxfY291bnQqKiAtIGNvdW50IG9mIGFsbCBwaXhlbHMgdGhhdCBmZWxsIHdpdGhpbiB0aGUgaHVlIGJhbmQsIHJlZ2FyZGxlc3Mgb2Ygc2F0dXJhdGlvbiBhbmQgbGlnaHRuZXNzDQoNCioqdmlzaWJcXyhodWUpXF9jb3VudCoqIC0gY291bnQgb2YgcGl4ZWxzIGluIHRoZSBodWUgYmFuZCBkZWVtZWQgYXMgInZpc2libHkgW2h1ZV0iIChzYXR1cmF0aW9uIG92ZXIgNDAlLCBsaWdodG5lc3MgYmV0d2VlbiAyMCUgYW5kIDc1JSkNCg0KKip2aXZpZFxfKGh1ZSlcX2NvdW50KiogLSBjb3VudCBvZiBwaXhlbHMgaW4gdGhlIGh1ZSBiYW5kIGRlZW1lZCBhcyAidml2aWRseSBbaHVlXSIgKHNhdHVyYXRpb24gb3ZlciA3MCUsIGxpZ2h0bmVzcyBiZXR3ZWVuIDMwJSBhbmQgNzAlKQ0KDQoqKipTYXR1cmF0aW9uIFN0YXRpc3RpY3MqKioNCg0KKipzYXRfbWluX3ZhbCoqIC0gbG93ZXN0IHNhdHVyYXRpb24gdmFsdWUgaW4gaW1hZ2UNCg0KKipzYXRfMjVfdmFsKiogLSAyNSUgcXVhcnRpbGUgdmFsdWUNCg0KKipzYXRfNTBfdmFsKiogLSBtZWRpYW4gc2F0dXJhdGlvbg0KDQoqKnNhdF83NV92YWwqKiAtIDc1JSBxdWFydGlsZSB2YWx1ZQ0KDQoqKnNhdF9tYXhfdmFsKiogLSBtb3N0IHNhdHVyYXRpb24NCg0KKioqSFNMIE1lYW4gVmFsdWVzKioqDQoNCioqaHVlX21lYW5fdmFsKiogLSBhdmVyYWdlIGh1ZSB2YWx1ZSAobm90IGluY3JlZGlibHkgbWVhbmluZ2Z1bCBvbiBhIGxvb3Bpbmcgc3BlY3RydW0pDQoNCioqc2F0X21lYW5fdmFsKiogLSBhdmVyYWdlIHNhdHVyYXRpb24gdmFsdWUNCg0KKipsaWdodF9tZWFuX3ZhbCoqIC0gYXZlcmFnZSBicmlnaHRuZXNzDQoNCioqKkxpZ2h0bmVzcyBTdGF0aXN0aWNzKioqDQoNCioqbGlnaHRfbWF4X3ZhbCoqIC0gYnJpZ2h0ZXN0IHZhbHVlDQoNCioqbGlnaHRfbWF4X2NvdW50KiogLSBxdWFudGl0eSBvZiBwaXhlbHMgd2l0aGluIDEuNSUgKGxpdGVyYWwpIG9mIHRoZSBtYXggbGlnaHRuZXNzIHZhbHVlDQoNCioqbGlnaHRfbWluX3ZhbCoqIC0gZGFya2VzdCB2YWx1ZQ0KDQoqKmxpZ2h0X21pbl9jb3VudCoqIC0gcXVhbnRpdHkgb2YgcGl4ZWxzIHdpdGhpbiAxLjUlIChsaXRlcmFsKSBvZiBoZSBtaW5pbXVtIGxpZ2h0bmVzcyB2YWx1ZSAoZGFya2VzdCkNCg0KKipsaWdodF8yNV92YWx1ZSoqIC0gMjUlIHF1YXJ0aWxlIHZhbHVlDQoNCioqbGlnaHRfNTBfdmFsdWUqKiAtIG1lZGlhbiBicmlnaHRuZXNzDQoNCioqbGlnaHRfNzVfdmFsdWUqKiAtIDc1JSBxdWFydGlsZSB2YWx1ZQ0KDQoqKmdlbl9icmlnaHRfY291bnQqKiAtIHF1YW50aXR5IG9mIHBpeGVscyB3aXRoIG92ZXIgODUlIGxpZ2h0bmVzcw0KDQoqKmdlbl9kYXJrX2NvdW50KiogLSBxdWFudGl0eSBvZiBwaXhlbHMgd2l0aCB1bmRlciAxNSUgYnJpZ2h0bmVzcw0KDQoqKmNvbW1vbl9oc2xcXygxLTQpXF92YWwqKiAtICooNCBjb2x1bW5zKSh0dXBsZSkqIGZvdXIgbW9zdCBjb21tb24gSFNMIHZhbHVlcw0KDQoqKmNvbW1vbl9oc2xcXygxLTQpXF9jb3VudCoqIC0gKig0IGNvbHVtbnMpKiBxdWFudGl0aWVzIG9mIHRoZSBmb3VyIG1vc3QgY29tbW9uIEhTTCB2YWx1ZXMNCg0KIyMjIyBEYXRhIENvbGxhdGlvbg0KDQpEdWUgdG8gY29sbGVjdGluZyBpbWFnZSBwcm9jZXNzaW5nIGRhdGEgb24gbXVsdGlwbGUgY29tcHV0ZXJzLCBtdWx0aXBsZSBmaWxlcyB3ZXJlIGNyZWF0ZWQgZm9yIGV4aWYgYW5kIGltYWdlIGRhdGEgLS0gcGFydGx5IGJ5IGRlc2lnbiBhbmQgcGFydHkgZHVlIHRvIG9jY2FzaW9uYWwgcmVhZC93cml0ZSBjb25mbGljdHMgb24gc2hhcmVkIGZpbGVzLiBBbGwgcmVjb3JkcyB3ZXJlIGdhdGhlcmVkIGludG8gRXhjZWwgYW5kIGNoZWNrZWQgZm9yIGR1cGxpY2F0ZXMgYmVmb3JlIGV4cG9ydGluZyBhcyBDU1ZzLg0KDQojIEh5cG90aGVzZXMNCg0KIyMjIEgxIC0gSHVlIHZzIERhdGUNCg0KJEhvOiQgVGhlcmUgaXMgbm8gY29ycmVsYXRpb24gYmV0d2VlbiB0aW1lIG9mIHllYXIgYW5kIGNvbG9yIHZhbHVlcw0KDQokSGE6JCBXYXJtIGNvbG9yIHZhbHVlcyBhcmUgbW9yZSBwcm9taW5lbnQgYmV0d2VlbiBNYXkgYW5kIFNlcHRlbWJlcg0KDQojIyMgSDIgLSBMaWdodG5lc3MgdnMgVGltZQ0KDQokSG86JCBUaGVyZSBpcyBubyBjb3JyZWxhdGlvbiBiZXR3ZWVuIHRpbWUgb2YgZGF5IGFuZCBsaWdodG5lc3MgdmFsdWVzDQoNCiRIYTokIExpZ2h0bmVzcyB2YWx1ZXMgYXJlIGhpZ2hlciBiZXR3ZWVuIDYgYW0gYW5kIDZwbQ0KDQojIyMgSDMgLSBTYXR1cmF0aW9uIHZzIFN1YmplY3QNCg0KJEhvOiQgVGhlcmUgaXMgbm8gY29ycmVsYXRpb24gYmV0d2VlbiBzYXR1cmF0aW9uIGFuZCBiZWluZyBhIHBpY3R1cmUgb2YgbXkgY2F0DQoNCiRIYTokIExvdyBzYXR1cmF0aW9uIHZhbHVlcyBhcmUgaW5jcmVhc2luZ2x5IGNvbW1vbiBvdmVyIHRpbWUsIGVzcGVjaWFsbHkgaW4gY2VudHJhbCBzdWItaW1hZ2VzDQoNCiMjIyBINCAtIFZpdmlkbmVzcyB2cyBJbWFnZSBUeXBlDQoNCiRIbzokIFZpdmlkIHJhdGlvIChwZXJjZW50YWdlIG9mIHZpdmlkIHBpeGVscykgaXMgdW5pZm9ybWx5IGRpc3RyaWJ1dGVkIGFtb25nIGFsbCBTb2Z0d2FyZSB0eXBlcw0KDQokSGE6JCBWaXZpZCByYXRpbyBpcyBjb25zaXN0ZW50bHkgaGlnaGVzdCBpbiBTbG93IFNodXR0ZXIgQ2FtIHBob3RvcyB3aXRob3V0IEpGSUYgdmFsdWVzDQoNCiMgUg0KDQojIyBQcmVwYXJpbmcgdGhlIERhdGENCg0KIyMjIEltcG9ydHMNCg0KYGBge3J9DQoNCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShwbG90bHkpDQpsaWJyYXJ5KHJlc2hhcGUyKQ0KDQpgYGANCg0KYGBge3J9DQpleGlmIDwtIHJlYWRfY3N2KCJjYXBzdG9uZV9leGlmLmNzdiIpDQppbWdfZGF0YSA8LSByZWFkX2NzdigiY2Fwc3RvbmVfaW1nX2RhdGEuY3N2IikNCg0KIyBzcGVjKGV4aWYpDQojIHNwZWMoaW1nX2RhdGEpDQpgYGANCg0KIyMjIENsZWFuaW5nDQoNCioqUHJlIEltcG9ydDoqKlwNClN1Yi1pbWFnZSBkYXRhIGZvciBtYWluIGltYWdlcyAoMCkgd2FzIGJ1Z2dlZCBpbiB0aGUgZmlyc3QgaG91cnMgb2YgaW1hZ2UgcHJvY2Vzc2luZy4gVGhpcyB3YXMgZml4ZWQgaW4gRXhjZWwgZHVyaW5nIHRoZSBkYXRhIGNvbGxhdGlvbiBzdGFnZS4NCg0KKipFWElGOioqDQoNCi0gICBGaXggY29sdW1uIGhlYWRlcnMNCi0gICBEcm9wIGNvbHVtbnM6IGRhdGVfdGltZV9vcmlnaW5hbCwgbW9kaWZ5X2RhdGUsIGxlbnNfaW5mbywgZl9udW1iZXIsIGZvY2FsX2xlbmd0aA0KLSAgIE5BIHZhbHVlcyAtIEpGSUYgXDwtIDAswqAgc3ViamVjdF9hcmVhIFw8LSBcPmRlcGVuZHMgb24gZGF0YSB0eXBlXDwgIjAgMCAwIDAiDQoNCmBgYHtyfQ0KbmFtZXMoZXhpZikgPC0gZ3N1YigiKFthLXowLTldKShbQS1aXSkiLCAiXFwxX1xcMiIsIG5hbWVzKGV4aWYpKQ0KbmFtZXMoZXhpZikgPC0gbmFtZXMoZXhpZikgJT4lIHRvbG93ZXIoKQ0KDQpleGlmX3RpZHkgPC0gc2VsZWN0KGV4aWYsIC1jKGRhdGVfdGltZV9vcmlnaW5hbCwgbW9kaWZ5X2RhdGUsIGxlbnNfaW5mbywgZm51bWJlciwgZm9jYWxfbGVuZ3RoKSkNCmV4aWZfdGlkeSA8LSByZXBsYWNlX25hKGV4aWZfdGlkeSwgbGlzdChzdWJqZWN0X2FyZWEgPSAiMCAwIDAgMCIsIGpmaWZ2ZXJzaW9uID0gMCkpDQoNCmBgYA0KDQoqKkltZ19kYXRhOioqDQoNCi0gICBEcm9wIGNvbHVtbnM6IGZsaWNrciwgaW1nX2xvYywgdGhlX2ltYWdlLCBjcm9wX2Nvb3Jkcywgcl9tb2RlLCBiX21vZGUsIGdfbW9kZSwgaW1nX2hlaWdodCwgaW1nX3dpZHRoLCBkb19pbWdfYXQNCi0gICBOYSB2YWx1ZXMgLSBwb3N0XzItNl9oc2wgXDwtICgtMSwtMSwtMSkNCg0KYGBge3J9DQppbWdzZF90aWR5IDwtIHNlbGVjdChpbWdfZGF0YSwgLWMoZmxpY2tyLCBpbWdfbG9jLCB0aGVfaW1hZ2UsIGltZ193aWR0aCwgaW1nX2hlaWdodCwgY3JvcF9jb29yZHMsIGRvX2ltZ19hdCwgcl9tb2RlLCBiX21vZGUsIGdfbW9kZSkpDQoNCmltZ3NkX3RpZHkgPC0gcmVwbGFjZV9uYShpbWdzZF90aWR5LCBsaXN0KA0KICBwb3N0XzJfaHNsID0gIigtMSwgLTEsIC0xKSIsDQogIHBvc3RfM19oc2wgPSAiKC0xLCAtMSwgLTEpIiwNCiAgcG9zdF80X2hzbCA9ICIoLTEsIC0xLCAtMSkiLA0KICBwb3N0XzVfaHNsID0gIigtMSwgLTEsIC0xKSIsDQogIHBvc3RfNl9oc2wgPSAiKC0xLCAtMSwgLTEpIg0KICApDQogICkNCg0KYGBgDQoNCiMjIyBCYXNpYyBGZWF0dXJlIEVuZ2luZWVyaW5nDQoNCioqRVhJRioqDQoNCi0gICBTcGxpdCBkYXRlX3RpbWVfb3JpZ2luYWwgdG8geWVhciwgbW9udGgtZGF5LCB0aW1lIGNvbHVtbnMNCi0gICBEYXRlIGNvbHVtbiB1c2VzIGEgcGxhY2Vob2xkZXIgeWVhciBzbyBtb250aC10by1tb250aCBjb21wYXJpc29ucyBhcmUgY29uc2lzdGVudA0KDQpgYGB7cn0NCg0KZXhpZl90aWR5IDwtIGV4aWZfdGlkeSAlPiUgc2VwYXJhdGUoY3JlYXRlX2RhdGUsIGludG8gPSBjKCdmdWxsX2RhdGUnLCAndGltZScpLCBzZXAgPSAiICIsIHJlbW92ZSA9IFRSVUUpICU+JSBzZXBhcmF0ZShmdWxsX2RhdGUsIGludG8gPSBjKCd5ZWFyJywgJ21vbnRoJywgJ2RheScpLCBzZXAgPSAiOiIsIHJlbW92ZSA9IEZBTFNFKSANCg0KZXhpZl90aWR5JGRhdGUgPC0gYXMuRGF0ZShwYXN0ZSgiMTg4MSIsIGV4aWZfdGlkeSRtb250aCwgZXhpZl90aWR5JGRheSwgc2VwID0gIi0iKSwgZm9ybWF0ID0iJVktJW0tJWQiKQ0KYGBgDQoNCioqSW1nX2RhdGEqKg0KDQotICAgQ291bnQgYnkgZmxpY2tyIGlkDQoNCiAgICAtICAgQWRkIGNvdW50KGZsaWNrcl9pZCkgdG8gRVhJRg0KICAgIC0gICBGbGFnIGNvdW50cyB1bmRlciAxMA0KICAgIC0gICBGbGFnIGNvdW50cyB1bmRlciA2IChpZTogc3ViaW1hZ2UgNSBub3QgYXZhaWxhYmxlKQ0KICAgIC0gICBPdXRwdXQgbGlzdCBvZiBhbGwgZmxpY2tySURzIHdpdGggbGVzcyB0aGFuIDEwIHJlY29yZHMgZm9yIGFkZGl0aW9uYWwgKGZ1dHVyZSkgcHJvY2Vzc2luZw0KDQpgYGB7cn0NCg0Kc3ViaW1nX3F0eSA8LSBpbWdzZF90aWR5ICU+JSBjb3VudCh1c2luZ19pZCkNCg0KYGBgDQoNClRvIG15IChoYXBweSkgc3VycHJpc2UsIG9ubHkgNSBpbWFnZXMgaGF2ZSBsZXNzIHRoYW4gMTAgcmVzdWx0cyBhbmQgb25seSAyIGhhdmUgbGVzcyB0aGFuIDYuIEluIHRoZSBpbnRlcmVzdCBvZiB0aW1lLCBJJ20gbm90aW5nIHRoZXNlIElEcyBieSBoYW5kIGFuZCBzaW1wbHkgcmVtb3ZpbmcgdGhlbSBmcm9tIG15IHdvcmtpbmcgZGF0YQ0KDQpgYGB7cn0NCg0KZ29vZF9pZHMgPC0gc3ViaW1nX3F0eVtzdWJpbWdfcXR5JG4gPj02LCAidXNpbmdfaWQiXQ0KDQppbWdzZF90aWR5IDwtIGltZ3NkX3RpZHkgJT4lIGZpbHRlcih1c2luZ19pZCAlaW4lIGdvb2RfaWRzJHVzaW5nX2lkKQ0KDQpleGlmX3RpZHkgPC0gZXhpZl90aWR5ICU+JSBmaWx0ZXIoZmxpY2tyX2lkICVpbiUgZ29vZF9pZHMkdXNpbmdfaWQpDQoNCmBgYA0KDQotICAgU3BsaXQgcGl4ZWwgbGlzdHMvdHVwbGVzIGZvciAzLWQgbWFwcGluZyAoc29sZWx5IHRoZSBkb21haW4gb2YgcGFydCAxKQ0KDQotICAgKFJlKXNldCBjb3VudCB2YWx1ZXMgYXMgaW50ZWdlcnMNCg0KLSAgIFRvdGFsIHBpeGVscyA9IGZ1bGxcXyhodWUpXF9jb3VudChzKSAoZGltZW5zaW9uIGRhdGEgaW5jb3JyZWN0IGZvciBmaXJzdCAxNDAwIHJlY29yZHMpDQoNCi0gICBHZW5lcmF0ZSByYXRpbyBjb2x1bW5zDQoNCmBgYHtyfQ0KDQojIGFmdGVyIHdvcmtpbmcgd2l0aCB0aGlzIGRhdGEgZm9yIDEyIGhvdXJzLCBJIGZpbmQgb3V0IHRoZXJlIGFyZSBoaWRkZW4gY2hhcmFjZXRlcnMgaW4gbXkgZGF0YSB0aGF0IHdlcmUgbm90IGRpc3BsYXlpbmcgaW4gRXhjZWwuDQoNCmltZ3NkX3RpZHkkcG9zdF90b3BfY291bnQgPC0gYXMuaW50ZWdlcihpbWdzZF90aWR5JHBvc3RfdG9wX2NvdW50KQ0KaW1nc2RfdGlkeSRwb3N0XzJfY291bnQgPC0gYXMuaW50ZWdlcihpbWdzZF90aWR5JHBvc3RfMl9jb3VudCkNCmltZ3NkX3RpZHkkcG9zdF8zX2NvdW50IDwtIGFzLmludGVnZXIoaW1nc2RfdGlkeSRwb3N0XzNfY291bnQpDQppbWdzZF90aWR5JHBvc3RfNF9jb3VudCA8LSBhcy5pbnRlZ2VyKGltZ3NkX3RpZHkkcG9zdF80X2NvdW50KQ0KaW1nc2RfdGlkeSRwb3N0XzVfY291bnQgPC0gYXMuaW50ZWdlcihpbWdzZF90aWR5JHBvc3RfNV9jb3VudCkNCmltZ3NkX3RpZHkkcG9zdF82X2NvdW50IDwtIGFzLmludGVnZXIoaW1nc2RfdGlkeSRwb3N0XzZfY291bnQpDQoNCmltZ3NkX3RpZHkkY29tbW9uX2hzbF8xX2NvdW50IDwtIGFzLmludGVnZXIoaW1nc2RfdGlkeSRjb21tb25faHNsXzFfY291bnQpDQppbWdzZF90aWR5JGNvbW1vbl9oc2xfMl9jb3VudCA8LSBhcy5pbnRlZ2VyKGltZ3NkX3RpZHkkY29tbW9uX2hzbF8yX2NvdW50KQ0KaW1nc2RfdGlkeSRjb21tb25faHNsXzNfY291bnQgPC0gYXMuaW50ZWdlcihpbWdzZF90aWR5JGNvbW1vbl9oc2xfM19jb3VudCkNCmltZ3NkX3RpZHkkY29tbW9uX2hzbF80X2NvdW50IDwtIGFzLmludGVnZXIoaW1nc2RfdGlkeSRjb21tb25faHNsXzRfY291bnQpDQoNCmltZ3NkX3RpZHkkZnVsbF9yZWRfY291bnQgPC0gYXMuaW50ZWdlcihpbWdzZF90aWR5JGZ1bGxfcmVkX2NvdW50KQ0KaW1nc2RfdGlkeSRmdWxsX29yYW5nZV9jb3VudCA8LSBhcy5pbnRlZ2VyKGltZ3NkX3RpZHkkZnVsbF9vcmFuZ2VfY291bnQpDQppbWdzZF90aWR5JGZ1bGxfeWVsbG93X2NvdW50IDwtIGFzLmludGVnZXIoaW1nc2RfdGlkeSRmdWxsX3llbGxvd19jb3VudCkNCmltZ3NkX3RpZHkkZnVsbF9ncmVlbl9jb3VudCA8LSBhcy5pbnRlZ2VyKGltZ3NkX3RpZHkkZnVsbF9ncmVlbl9jb3VudCkNCmltZ3NkX3RpZHkkZnVsbF9jeWFuX2NvdW50IDwtIGFzLmludGVnZXIoaW1nc2RfdGlkeSRmdWxsX2N5YW5fY291bnQpDQppbWdzZF90aWR5JGZ1bGxfYmx1ZV9jb3VudCA8LSBhcy5pbnRlZ2VyKGltZ3NkX3RpZHkkZnVsbF9ibHVlX2NvdW50KQ0KaW1nc2RfdGlkeSRmdWxsX3B1cnBsZV9jb3VudCA8LSBhcy5pbnRlZ2VyKGltZ3NkX3RpZHkkZnVsbF9wdXJwbGVfY291bnQpDQppbWdzZF90aWR5JGZ1bGxfbWFnX2NvdW50IDwtIGFzLmludGVnZXIoaW1nc2RfdGlkeSRmdWxsX21hZ19jb3VudCkNCmltZ3NkX3RpZHkkdmlzaWJfcmVkX2NvdW50IDwtIGFzLmludGVnZXIoaW1nc2RfdGlkeSR2aXNpYl9yZWRfY291bnQpDQppbWdzZF90aWR5JHZpc2liX29yYW5nZV9jb3VudCA8LSBhcy5pbnRlZ2VyKGltZ3NkX3RpZHkkdmlzaWJfb3JhbmdlX2NvdW50KQ0KaW1nc2RfdGlkeSR2aXNpYl95ZWxsb3dfY291bnQgPC0gYXMuaW50ZWdlcihpbWdzZF90aWR5JHZpc2liX3llbGxvd19jb3VudCkNCmltZ3NkX3RpZHkkdmlzaWJfZ3JlZW5fY291bnQgPC0gYXMuaW50ZWdlcihpbWdzZF90aWR5JHZpc2liX2dyZWVuX2NvdW50KQ0KaW1nc2RfdGlkeSR2aXNpYl9jeWFuX2NvdW50IDwtIGFzLmludGVnZXIoaW1nc2RfdGlkeSR2aXNpYl9jeWFuX2NvdW50KQ0KaW1nc2RfdGlkeSR2aXNpYl9ibHVlX2NvdW50IDwtIGFzLmludGVnZXIoaW1nc2RfdGlkeSR2aXNpYl9ibHVlX2NvdW50KQ0KaW1nc2RfdGlkeSR2aXNpYl9wdXJwbGVfY291bnQgPC0gYXMuaW50ZWdlcihpbWdzZF90aWR5JHZpc2liX3B1cnBsZV9jb3VudCkNCmltZ3NkX3RpZHkkdmlzaWJfbWFnX2NvdW50IDwtIGFzLmludGVnZXIoaW1nc2RfdGlkeSR2aXNpYl9tYWdfY291bnQpDQppbWdzZF90aWR5JHZpdmlkX3JlZF9jb3VudCA8LSBhcy5pbnRlZ2VyKGltZ3NkX3RpZHkkdml2aWRfcmVkX2NvdW50KQ0KaW1nc2RfdGlkeSR2aXZpZF9vcmFuZ2VfY291bnQgPC0gYXMuaW50ZWdlcihpbWdzZF90aWR5JHZpdmlkX29yYW5nZV9jb3VudCkNCmltZ3NkX3RpZHkkdml2aWRfeWVsbG93X2NvdW50IDwtIGFzLmludGVnZXIoaW1nc2RfdGlkeSR2aXZpZF95ZWxsb3dfY291bnQpDQppbWdzZF90aWR5JHZpdmlkX2dyZWVuX2NvdW50IDwtIGFzLmludGVnZXIoaW1nc2RfdGlkeSR2aXZpZF9ncmVlbl9jb3VudCkNCmltZ3NkX3RpZHkkdml2aWRfY3lhbl9jb3VudCA8LSBhcy5pbnRlZ2VyKGltZ3NkX3RpZHkkdml2aWRfY3lhbl9jb3VudCkNCmltZ3NkX3RpZHkkdml2aWRfYmx1ZV9jb3VudCA8LSBhcy5pbnRlZ2VyKGltZ3NkX3RpZHkkdml2aWRfYmx1ZV9jb3VudCkNCmltZ3NkX3RpZHkkdml2aWRfcHVycGxlX2NvdW50IDwtIGFzLmludGVnZXIoaW1nc2RfdGlkeSR2aXZpZF9wdXJwbGVfY291bnQpDQppbWdzZF90aWR5JHZpdmlkX21hZ19jb3VudCA8LSBhcy5pbnRlZ2VyKGltZ3NkX3RpZHkkdml2aWRfbWFnX2NvdW50KQ0KaW1nc2RfdGlkeSR2aXZpZF9jb3VudCA8LSBhcy5pbnRlZ2VyKGltZ3NkX3RpZHkkdml2aWRfY291bnQpDQoNCmltZ3NkX3RpZHkkZ2VuX2JyaWdodF9jb3VudCA8LSBhcy5pbnRlZ2VyKGltZ3NkX3RpZHkkZ2VuX2JyaWdodF9jb3VudCkNCmltZ3NkX3RpZHkkZ2VuX2RhcmtfY291bnQgPC0gYXMuaW50ZWdlcihpbWdzZF90aWR5JGdlbl9kYXJrX2NvdW50KQ0KDQppbWdzZF90aWR5IDwtIGltZ3NkX3RpZHkgJT4lIG11dGF0ZSh0b3RhbF9waXhlbHMgPSBmdWxsX3JlZF9jb3VudCArDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZ1bGxfb3JhbmdlX2NvdW50ICsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZnVsbF95ZWxsb3dfY291bnQgKw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmdWxsX2dyZWVuX2NvdW50ICsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZnVsbF9jeWFuX2NvdW50ICsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZnVsbF9ibHVlX2NvdW50ICsgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZ1bGxfcHVycGxlX2NvdW50ICsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZnVsbF9tYWdfY291bnQpDQoNCiMgbW9yZSB0eXBlIGNvbnZlcnNpb25zDQppbWdzZF90aWR5JHRvdGFsX3BpeGVscyA8LSBhcy5pbnRlZ2VyKGltZ3NkX3RpZHkkdG90YWxfcGl4ZWxzKQ0KaW1nc2RfdGlkeSRsaWdodF9tZWFuX3ZhbCA8LSBhcy5udW1lcmljKGltZ3NkX3RpZHkkbGlnaHRfbWVhbl92YWwpDQppbWdzZF90aWR5JHBvc3RfbnVtX3JlZ2lvbnMgPC0gYXMuaW50ZWdlcihpbWdzZF90aWR5JHBvc3RfbnVtX3JlZ2lvbnMpDQppbWdzZF90aWR5JHJfbWVhbiA8LSBhcy5udW1lcmljKGltZ3NkX3RpZHkkcl9tZWFuKQ0KaW1nc2RfdGlkeSRnX21lYW4gPC0gYXMubnVtZXJpYyhpbWdzZF90aWR5JGdfbWVhbikNCmltZ3NkX3RpZHkkYl9tZWFuIDwtIGFzLm51bWVyaWMoaW1nc2RfdGlkeSRiX21lYW4pDQoNCg0KI2VzdGFibGlzaGluZyByYXRpbyBjb2x1bW5zDQppbWdzZF9yYXRpbyA8LSBpbWdzZF90aWR5ICU+JSANCiAgbXV0YXRlKHJhdGlvX3Bvc3RfdG9wX2hzbCA9IHBvc3RfdG9wX2NvdW50IC8gdG90YWxfcGl4ZWxzKSAlPiUNCiAgbXV0YXRlKHJhdGlvX3Bvc3RfMl9oc2wgPSBwb3N0XzJfY291bnQgLyB0b3RhbF9waXhlbHMpICU+JQ0KICBtdXRhdGUocmF0aW9fcG9zdF8zX2hzbCA9IHBvc3RfM19jb3VudCAvIHRvdGFsX3BpeGVscykgJT4lDQogIG11dGF0ZShyYXRpb19wb3N0XzRfaHNsID0gcG9zdF80X2NvdW50IC8gdG90YWxfcGl4ZWxzKSAlPiUNCiAgbXV0YXRlKHJhdGlvX3Bvc3RfNV9oc2wgPSBwb3N0XzVfY291bnQgLyB0b3RhbF9waXhlbHMpICU+JQ0KICBtdXRhdGUocmF0aW9fcG9zdF82X2hzbCA9IHBvc3RfNl9jb3VudCAvIHRvdGFsX3BpeGVscykgJT4lDQogIG11dGF0ZShyYXRpb19mdWxsX3JlZCA9IGZ1bGxfcmVkX2NvdW50IC8gdG90YWxfcGl4ZWxzKSAlPiUNCiAgbXV0YXRlKHJhdGlvX2Z1bGxfb3JhZ25lID0gZnVsbF9vcmFuZ2VfY291bnQgLyB0b3RhbF9waXhlbHMpICU+JQ0KICBtdXRhdGUocmF0aW9fZnVsbF95ZWxsb3cgPSBmdWxsX3llbGxvd19jb3VudCAvIHRvdGFsX3BpeGVscykgJT4lDQogIG11dGF0ZShyYXRpb19mdWxsX2dyZWVuID0gZnVsbF9ncmVlbl9jb3VudCAvIHRvdGFsX3BpeGVscykgJT4lICANCiAgbXV0YXRlKHJhdGlvX2Z1bGxfY3lhbiA9IGZ1bGxfY3lhbl9jb3VudCAvIHRvdGFsX3BpeGVscykgJT4lICANCiAgbXV0YXRlKHJhdGlvX2Z1bGxfYmx1ZSA9IGZ1bGxfYmx1ZV9jb3VudCAvIHRvdGFsX3BpeGVscykgJT4lICANCiAgbXV0YXRlKHJhdGlvX2Z1bGxfcHVycGxlID0gZnVsbF9wdXJwbGVfY291bnQgLyB0b3RhbF9waXhlbHMpICU+JSAgDQogIG11dGF0ZShyYXRpb19mdWxsX21hZyA9IGZ1bGxfbWFnX2NvdW50IC8gdG90YWxfcGl4ZWxzKSAlPiUgIA0KICBtdXRhdGUocmF0aW9fZnVsbF9yZWQgPSBmdWxsX3JlZF9jb3VudCAvIHRvdGFsX3BpeGVscykgJT4lDQogIG11dGF0ZShyYXRpb192aXNpYl9yZWQgPSB2aXNpYl9yZWRfY291bnQgLyB0b3RhbF9waXhlbHMpICU+JQ0KICBtdXRhdGUocmF0aW9fdmlzaWJfb3JhZ25lID0gdmlzaWJfb3JhbmdlX2NvdW50IC8gdG90YWxfcGl4ZWxzKSAlPiUNCiAgbXV0YXRlKHJhdGlvX3Zpc2liX3llbGxvdyA9IHZpc2liX3llbGxvd19jb3VudCAvIHRvdGFsX3BpeGVscykgJT4lDQogIG11dGF0ZShyYXRpb192aXNpYl9ncmVlbiA9IHZpc2liX2dyZWVuX2NvdW50IC8gdG90YWxfcGl4ZWxzKSAlPiUgIA0KICBtdXRhdGUocmF0aW9fdmlzaWJfY3lhbiA9IHZpc2liX2N5YW5fY291bnQgLyB0b3RhbF9waXhlbHMpICU+JSAgDQogIG11dGF0ZShyYXRpb192aXNpYl9ibHVlID0gdmlzaWJfYmx1ZV9jb3VudCAvIHRvdGFsX3BpeGVscykgJT4lICANCiAgbXV0YXRlKHJhdGlvX3Zpc2liX3B1cnBsZSA9IHZpc2liX3B1cnBsZV9jb3VudCAvIHRvdGFsX3BpeGVscykgJT4lICANCiAgbXV0YXRlKHJhdGlvX3Zpc2liX21hZyA9IHZpc2liX21hZ19jb3VudCAvIHRvdGFsX3BpeGVscykgJT4lDQogIG11dGF0ZShyYXRpb192aXZpZF9yZWQgPSB2aXZpZF9yZWRfY291bnQgLyB0b3RhbF9waXhlbHMpICU+JQ0KICBtdXRhdGUocmF0aW9fdml2aWRfb3JhZ25lID0gdml2aWRfb3JhbmdlX2NvdW50IC8gdG90YWxfcGl4ZWxzKSAlPiUNCiAgbXV0YXRlKHJhdGlvX3ZpdmlkX3llbGxvdyA9IHZpdmlkX3llbGxvd19jb3VudCAvIHRvdGFsX3BpeGVscykgJT4lDQogIG11dGF0ZShyYXRpb192aXZpZF9ncmVlbiA9IHZpdmlkX2dyZWVuX2NvdW50IC8gdG90YWxfcGl4ZWxzKSAlPiUgIA0KICBtdXRhdGUocmF0aW9fdml2aWRfY3lhbiA9IHZpdmlkX2N5YW5fY291bnQgLyB0b3RhbF9waXhlbHMpICU+JSAgDQogIG11dGF0ZShyYXRpb192aXZpZF9ibHVlID0gdml2aWRfYmx1ZV9jb3VudCAvIHRvdGFsX3BpeGVscykgJT4lICANCiAgbXV0YXRlKHJhdGlvX3ZpdmlkX3B1cnBsZSA9IHZpdmlkX3B1cnBsZV9jb3VudCAvIHRvdGFsX3BpeGVscykgJT4lICANCiAgbXV0YXRlKHJhdGlvX3ZpdmlkX21hZyA9IHZpdmlkX21hZ19jb3VudCAvIHRvdGFsX3BpeGVscykgJT4lDQogIG11dGF0ZShyYXRpb192aXZpZCA9IHZpdmlkX2NvdW50IC8gdG90YWxfcGl4ZWxzKQ0KDQoNCg0KDQpgYGANCg0KIyMgRXhwbG9yYXRpb24NCg0KIyMjIDJEIFNjYXR0ZXINCg0KLSAgIGZyZXF1ZW5jeSBvZiBtb3N0IGNvbW1vbiBwb3N0ZXJpemVkIGNvbG9yIHZzIG1vc3QgY29tbW9uIG5hdGl2ZSBjb2xvcg0KDQpgYGB7cn0NCg0KZmlnIDwtIHBsb3RfbHkoaW1nc2RfdGlkeSwgDQogICAgICAgICAgICAgICB4PSB+cG9zdF90b3BfY291bnQsIA0KICAgICAgICAgICAgICAgeT0gfmNvbW1vbl9oc2xfMV9jb3VudCwgDQogICAgICAgICAgICAgICB0eXBlID0gInNjYXR0ZXIiLCBtb2RlPSJtYXJrZXJzIiwgc2l6ZSA9IDIsIGNvbG9yID0gfnN1Yl9pbWcpDQoNCmZpZw0KYGBgDQoNCi0gICBMaWdodCBtaW4geCBsaWdodCBtYXgNCg0KYGBge3J9DQpmaWcgPC0gcGxvdF9seShpbWdzZF90aWR5LCANCiAgICAgICAgICAgICAgIHg9fmxpZ2h0X21pbl92YWwsIA0KICAgICAgICAgICAgICAgeT0gfmxpZ2h0X21heF92YWwsIA0KICAgICAgICAgICAgICAgdHlwZSA9ICJzY2F0dGVyIiwgbW9kZT0ibWFya2VycyIsIHNpemUgPSAyLCBjb2xvciA9IH5zdWJfaW1nKQ0KDQpmaWcNCmBgYA0KDQotICAgR2VuIGJyaWdodCB4IGdlbiBkYXJrDQoNCmBgYHtyfQ0KDQpmaWcgPC0gcGxvdF9seShpbWdzZF90aWR5LCANCiAgICAgICAgICAgICAgIHg9fmdlbl9icmlnaHRfY291bnQsIA0KICAgICAgICAgICAgICAgeT0gfmdlbl9kYXJrX2NvdW50LCANCiAgICAgICAgICAgICAgIHR5cGUgPSAic2NhdHRlciIsIG1vZGU9Im1hcmtlcnMiLCBzaXplID0gMiwgY29sb3IgPSB+c3ViX2ltZykNCg0KZmlnDQpgYGANCg0KLSAgIFNhdCBtaW4geCBzYXQgbWF4DQoNCmBgYHtyfQ0KDQpmaWcgPC0gcGxvdF9seShpbWdzZF90aWR5LCANCiAgICAgICAgICAgICAgIHg9fnNhdF9taW5fdmFsLCANCiAgICAgICAgICAgICAgIHk9IH5zYXRfbWF4X3ZhbCwgDQogICAgICAgICAgICAgICB0eXBlID0gInNjYXR0ZXIiLCBtb2RlPSJtYXJrZXJzIiwgc2l6ZSA9IDIsIGNvbG9yID0gfnN1Yl9pbWcpDQoNCmZpZw0KDQpgYGANCg0KIyMjIDFEIEhpc3RvZ3JhbXMNCg0KLSAgIFZpdmlkICUNCg0KRHVlIHRvIGEgdmVyeSBsYXJnZSBudW1iZXIgb2YgaW1hZ2VzIHRoYXQgaGF2ZSBmZXdlciB0aGFuIDIlIHZpdmlkIHBpeGVscywgVml2aWQgUmF0aW9zIGFyZSBzZXBhcmF0ZWQgb3V0IGludG8gdGhyZWUgc2VnbWVudHM6IDAlIHZpdmlkIHBpeGVscywgMC0xJSB2aXZpZCBwaXhlbHMsIGFuZCBtb3JlIHRoYW4gMSUgdml2aWQgcGl4ZWxzDQoNCmBgYHtyfQ0KDQp2aXZpZF9yYXRpbyA8LSBpbWdzZF9yYXRpbyAlPiUgc2VsZWN0KHJhdGlvX3ZpdmlkKQ0Kdml2aWRfcmF0aW8kcmF0aW9fdml2aWQgPC0gYXMubnVtZXJpYyh2aXZpZF9yYXRpbyRyYXRpb192aXZpZCkNCg0Kdml2aWRfcmF0aW8gPC0gdml2aWRfcmF0aW8gJT4lIG11dGF0ZSgNCiAgcmF0aW9fcmFuayA9IGNhc2Vfd2hlbigNCiAgICByYXRpb192aXZpZCA9PSAwIH4gMCwgDQogICAgcmF0aW9fdml2aWQgPiAwICYgcmF0aW9fdml2aWQgPCAwLjAxIH4gMSwgDQogICAgcmF0aW9fdml2aWQgPj0gMC4wMSB+IDIpDQogICkNCg0KcnJfY291bnRzIDwtIGNvdW50KHZpdmlkX3JhdGlvLCByYXRpb19yYW5rKQ0KDQp2cnJfcGllIDwtIHBsb3RfbHkocnJfY291bnRzLCBsYWJlbHM9fmZhY3RvcihyYXRpb19yYW5rKSwgdmFsdWVzPSB+biwgdHlwZT0ncGllJykNCg0KdnJyX3BpZQ0KIyAwIC0+IE5vIFZpdmlkIFBpeGVscw0KIyAxIC0+IDAtMSUgdml2aWQgcGl4ZWxzDQojIDIgLT4gbW9yZSB0aGFuIDElIHZpdmlkIHBpeGVscw0KYGBgDQoNCkxlc3Mgdml2aWQgcGl4ZWxzIHRoYW4gZXhwZWN0ZWQhIFdheSBsZXNzISBPbmx5IDM1OSBpbWFnZXMvc3ViaW1hZ2VzIGhhZCBtb3JlIHRoYW4gMSUgb2YgcGl4ZWxzIHdpdGggbW9yZSB0aGFuIDcwJSBzYXR1cmF0aW9uIGFuZCBsaWdodG5lc3MgYmV0d2VlbiA0MCBhbmQgNzAlDQoNCk5vdyBsZXQncyBnZXQgYmFjayB0byBzZWVpbmcgdGhlIGRpc3RyaWJ1dGlvbiBvZiB0aG9zZSAzNTkgdmFsdWVzOg0KDQpgYGB7cn0NCg0KDQp2aXZpZF9oaXN0IDwtIHZpdmlkX3JhdGlvICU+JSBmaWx0ZXIocmF0aW9fcmFuayA9PSAyKQ0KDQpmaWcgPC0gcGxvdF9seSh2aXZpZF9oaXN0LA0KICAgICAgICAgICAgICAgeD0gfnJhdGlvX3ZpdmlkLA0KICAgICAgICAgICAgICAgdHlwZSA9ICJoaXN0b2dyYW0iKQ0KDQpmaWcNCmBgYA0KDQpGdXR1cmUgaW52ZXN0aWdhdGlvbiB3aWxsIHBlcmZvcm0gdGhlc2UgY2FsY3VsYXRpb25zIG9uIHRoZSBtYWluIGRhdGFmcmFtZSBpbiBvcmRlciB0byBhY2Nlc3Mgc3BlY2lmaWMgaW1hZ2UgSURzIGFuZCBldmFsdWF0ZSB0aGUgc3ViamVjdGl2ZSBxdWFsaXRpZXMgb2YgdGhlIHRvcCB2aXZpZCBpbWFnZXMuDQoNCi0gICBQb3N0LW51bSAoZmlsdGVyIGJ5IGltZyBzZWdtZW50KQ0KDQpJbnZlc3RpZ2F0aW5nIHRoZSBkaXN0cmlidXRpb24gb2YgaG93IG1hbnkgcG9zdGVyaXphdGlvbiAiY2x1bXBzIiB3ZXJlIGluIGVhY2ggaW1hZ2UNCg0KYGBge3J9DQoNCiNmaXJzdCBzZXR0aW5nIHVwIGZpbHRlcnMgZm9yIG1vcmUgdXNlZnVsIGZhY2V0aW5nIGdvaW5nIGZvcndhcmQNCnJfZmlnX2ZpbHRlcl8wIDwtIGltZ3NkX3JhdGlvICU+JSBmaWx0ZXIoaW1nc2RfcmF0aW8kc3ViX2ltZyA9PSAwKQ0Kcl9maWdfZmlsdGVyX2EgPC0gaW1nc2RfcmF0aW9baW1nc2RfcmF0aW8kc3ViX2ltZyAlaW4lIGMoMSwgMiwgMyksIF0NCnJfZmlnX2ZpbHRlcl9iIDwtIGltZ3NkX3JhdGlvW2ltZ3NkX3JhdGlvJHN1Yl9pbWcgJWluJSBjKDQsIDUsIDYpLCBdDQpyX2ZpZ19maWx0ZXJfYyA8LSBpbWdzZF9yYXRpb1tpbWdzZF9yYXRpbyRzdWJfaW1nICVpbiUgYyg3LCA4LCA5KSwgXQ0KDQoNCmZpZ18wIDwtIGdncGxvdChyX2ZpZ19maWx0ZXJfMCwNCiAgICAgICAgICAgICAgIGFlcyh4PXBvc3RfbnVtX3JlZ2lvbnMpKSArIA0KICAgICAgICAgICAgICAgIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gNSkNCg0KZmlnX2EgPC0gZ2dwbG90KHJfZmlnX2ZpbHRlcl9hLA0KICAgICAgICAgICAgICBhZXMoeD1wb3N0X251bV9yZWdpb25zKSkgKyANCiAgICAgICAgICAgICAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSA1KSArDQogICAgICAgICAgICAgIGZhY2V0X2dyaWQgKH4gc3ViX2ltZykNCg0KZmlnX2IgPC0gZ2dwbG90KHJfZmlnX2ZpbHRlcl9iLA0KICAgICAgICAgICAgICBhZXMoeD1wb3N0X251bV9yZWdpb25zKSkgKyANCiAgICAgICAgICAgICAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSA1KSArDQogICAgICAgICAgICAgIGZhY2V0X2dyaWQgKH4gc3ViX2ltZykNCg0KZmlnX2MgPC0gZ2dwbG90KHJfZmlnX2ZpbHRlcl9jLA0KICAgICAgICAgICAgICBhZXMoeD1wb3N0X251bV9yZWdpb25zKSkgKyANCiAgICAgICAgICAgICAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSA1KSArDQogICAgICAgICAgICAgIGZhY2V0X2dyaWQgKH4gc3ViX2ltZykNCg0KZmlnXzBiIDwtIGdncGxvdChyX2ZpZ19maWx0ZXJfMCwNCiAgICAgICAgICAgICAgIGFlcyh4PXBvc3RfbnVtX3JlZ2lvbnMpKSArIA0KICAgICAgICAgICAgICAgIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gNTApDQoNCmZpZ18wDQpmaWdfMGINCmZpZ19hDQpmaWdfYg0KZmlnX2MNCg0KYGBgDQoNCk5vIGh1Z2UgY29uY2x1c2lvbnMgdG8gZHJhdyBhYm91dCB0aGUgZGlzdHJpYnV0aW9uIGluIHN1Yl9pbWFnZSAwLCBvdGhlciB0aGFuIHRoZSB4LXNjYWxlIGlzIHNob3duIGFzIHJvdWdobHkgMTB4IChzbGlnaHRseSBsZXNzKSB0aGFuIHRoZSBzdWJpbWFnZXMuIEFsc28gbWlsZGx5IGludGVyZXN0aW5nIChhbHNvIG9uIHN1Yl9pbWcgMCksIHRoZSBtYXggY291bnQgZm9yIGFueSBnaXZlbiBiaW4gKGF0IGJpbi13aWR0aCA1KSwgaXMgYSBmcmFjdGlvbiBvZiB0aGUgc2NhbGUgb2YgdGhlIG90aGVyIHN1Yl9pbWFnZXMuIEJ1bXBpbmcgdGhlIHN1Yl9pbWcgMCBiaW53aWR0aCB1cCB0byA1MCAoMTB4KSwgYnJpbmdzIGl0IG1vcmUgaW4gbGluZSB3aXRoIHRoZSBjb3VudHMgaW4gdGhlIHN1Yl9pbWFnZXMsIGJ1dCB0aGUgbG9uZyB0YWlsIG9uIHN1Yl9pbWcgMCBrZWVwcyBpdCBmcm9tIG1hdGNoaW5nIGV4YWN0bHkuDQoNCk90aGVyd2lzZSwgdGhlIG9ubHkgbm90ZXdvcnRoeSBvYnNlcnZhdGlvbiBpcyB0aGUgbGVzcy1pbnRlbnNlIHJpZ2h0IHNrZXcgb24gc3ViX2ltYWdlcyAyLCA1LCBhbmQgOC4NCg0KVGhlc2Ugc3ViLWltYWdlcyByZXByZXNlbnQgdGhlIGhvcml6b250YWwgbWlkZGxlIHRoaXJkIG9mIHRoZWlyIHNvdXJjZSBpbWFnZXMsIG1pbWlja2luZyB0aGUgYXJ0aXN0aWMgcHJpbmNpcGxlcyBvZiB0aGUgcnVsZSBvZiB0aGlyZHMgYW5kIHB1dHRpbmcgZm9jYWwgcG9pbnRzIG5lYXIgdGhlIGNlbnRlciBvZiB0aGUgaW1hZ2UuDQoNCi0gICBFeGlmIGJyaWdodG5lc3MNCg0KRVhJRiBkYXRhLCBJIGhhdmUgbm90IGZvcnNha2VuIHlvdS4NCg0KYGBge3J9DQoNCg0KDQpmaWcgPC0gZ2dwbG90KGV4aWZfdGlkeSwNCiAgICAgICAgICAgICAgIGFlcyh4PWJyaWdodG5lc3NfdmFsdWUpKSArIA0KICAgICAgICAgICAgICAgIGdlb21faGlzdG9ncmFtKGJpbnM9MjUwKQ0KDQpmaWcNCmBgYA0KDQpUaGF0J3MgdGhlIG1vc3Qgbm9ybWFsIGRpc3RyaWJ1dGlvbiB3ZSd2ZSBzZWVuIHlldCEgQnV0IHN0aWxsIHByZXR0eSBpcnJlZ3VsYXIuIExldCdzIHNlZSBob3cgaXQgY29tcGFyZXMgdG8gdGhlIEhTTCBkYXRhDQoNCi0gICBNZWFuX2xpZ2h0bmVzcw0KDQpgYGB7cn0NCg0KZmlnX2FsbCA8LSBnZ3Bsb3QoaW1nc2RfcmF0aW8sDQogICAgICAgICAgICAgICBhZXMoeD1saWdodF9tZWFuX3ZhbCkpICsgDQogICAgICAgICAgICAgICAgZ2VvbV9oaXN0b2dyYW0oYmlucz0yNTApDQoNCmZpZ18wIDwtIGdncGxvdChyX2ZpZ19maWx0ZXJfMCwNCiAgICAgICAgICAgICAgIGFlcyh4PWxpZ2h0X21lYW5fdmFsKSkgKyANCiAgICAgICAgICAgICAgICBnZW9tX2hpc3RvZ3JhbShiaW5zPTI1MCkNCg0KZmlnX2EgPC0gZ2dwbG90KHJfZmlnX2ZpbHRlcl9hLA0KICAgICAgICAgICAgICBhZXMoeD1saWdodF9tZWFuX3ZhbCkpICsgDQogICAgICAgICAgICAgIGdlb21faGlzdG9ncmFtKGJpbnM9MjUwKSArDQogICAgICAgICAgICAgIGZhY2V0X2dyaWQofiBzdWJfaW1nKQ0KDQpmaWdfYiA8LSBnZ3Bsb3Qocl9maWdfZmlsdGVyX2IsDQogICAgICAgICAgICAgIGFlcyh4PWxpZ2h0X21lYW5fdmFsKSkgKyANCiAgICAgICAgICAgICAgZ2VvbV9oaXN0b2dyYW0oYmlucz0yNTApICsNCiAgICAgICAgICAgICAgZmFjZXRfZ3JpZCh+IHN1Yl9pbWcpDQoNCmZpZ19jIDwtIGdncGxvdChyX2ZpZ19maWx0ZXJfYywNCiAgICAgICAgICAgICAgYWVzKHg9bGlnaHRfbWVhbl92YWwpKSArIA0KICAgICAgICAgICAgICBnZW9tX2hpc3RvZ3JhbShiaW5zPTI1MCkgKw0KICAgICAgICAgICAgICBmYWNldF9ncmlkKH4gc3ViX2ltZykNCg0KDQpmaWdfYWxsDQpmaWdfMA0KZmlnX2ENCmZpZ19iDQpmaWdfYw0KYGBgDQoNCk9ic2VydmF0aW9uczogVmFyaWF0aW9uIGFtb25nIHN1Yi1pbWFnZXMgaXMgZmFpcmx5IG1pbm9yLCB3aXRoIHNvbWUgc29tZSBwb3NzaWJpbGl0eSB0aGF0IGNvcm5lciBxdWFkcmFudHMgKDEsIDMsIDcsIDkpIGhhdmUgd2lkZXIgZGlzdHJpYnV0aW9ucyBvdmVyYWxsLiBMaWdodG5lc3MgdmFsdWVzIHByb2Nlc3NlZCBmb3IgdGhpcyBkYXRhc2V0IGFyZSBjZW50ZXJlZCBhcm91bmQgMC42MjUgd2l0aCBhIHNsaWdodCBsZWZ0IHNrZXcuIFdoaWxlIHRoZSBFWElGIGRhdGEgaGFzIGEgbGVzcy1ub3JtYWwgZGlzdHJpYnV0aW9uLCBpdCBkb2VzIHNlZW0gdG8gaGF2ZSBhIGNlbnRlciBzbGlnaHRseSB0byB0aGUgcmlnaHQgb2YgaXRzIGJhc2VsaW5lIHZhbHVlLCBidXQgdGhlIGlycmVndWxhcml0eSBvZiB0aGUgc2hhcGUgbG9va3MgbW9yZSBsaWtlIGEgcmlnaHQgc2tldyB0aGFuIGxlZnQuDQoNCi0gICAoci9nL2IpIG1lYW5cDQoNCmBgYHtyfQ0KDQpmaWcgPC0gcGxvdF9seShpbWdzZF9yYXRpbywgYWxwaGEgPSAuNCkNCmZpZyA8LSBmaWcgJT4lIGFkZF9oaXN0b2dyYW0oeCA9IH5yX21lYW4pDQpmaWcgPC0gZmlnICU+JSBhZGRfaGlzdG9ncmFtKHggPSB+Z19tZWFuKQ0KZmlnIDwtIGZpZyAlPiUgYWRkX2hpc3RvZ3JhbSh4ID0gfmJfbWVhbikNCmZpZyA8LSBmaWcgJT4lIGxheW91dChiYXJtb2RlID0gJ292ZXJsYXknLCBjb2xvcndheT1jKCdyZWQnLCAnZ3JlZW4nLCAnYmx1ZScpKQ0KDQpmaWcNCmBgYA0KDQpPYnNlcnZhdGlvbjogTm8gc3BlY2lhbCBpbmZvcm1hdGlvbiBoZXJlLiBBcyBleHBlY3RlZCwgaXQgbG9va3MgYSBsb3QgbGlrZSB0aGUgbWVhbiBsaWdodG5lc3MgdmFsdWUgZGlzdHJidXRpb24uIEFsbW9zdCBhcyBpZiByK2crYiA9IGxpZ2h0Li4uLg0KDQotICAgRGF0ZSBkaXN0cmlidXRpb24gKHdpdGggYW5kIHdpdGhvdXQgeWVhcikNCg0KYGBge3J9DQoNCmZpZyA8LSBwbG90X2x5KGV4aWZfdGlkeSwgeCA9IH5kYXRlLCB0eXBlID0gJ2hpc3RvZ3JhbScpDQoNCmZpZ19hbGwgPC0gcGxvdF9seShleGlmX3RpZHksIHggPSB+ZnVsbF9kYXRlLCB0eXBlID0gJ2hpc3RvZ3JhbScpDQoNCmZpZw0KZmlnX2FsbA0KYGBgDQoNCk9ic2VydmF0aW9uOiBFdmVuIHdoZW4gYWRqdXN0aW5nIGZvciB5ZWFyLCB0aGUgZGlzdHJpYnV0aW9uIG9mIGltYWdlcyBpcyBoaWdobHkgaXJyZWd1bGFyLiBBcyBhIG1vbnRoLCBBdWd1c3QgaXMgb3Zlci1yZXByZXNlbnRlZCwgYW5kIGFzIGFuIGluZGl2aWR1YWwgZGF0ZSwgV2lsbCBhbmQgVGF5bG9yJ3Mgd2VkZGluZyBpcyBvdmVyLXJlcHJlc2VudGVkLg0KDQpgYGB7cn0NCmV4aWZfdGlkeSR0aW1lIDwtIHN0cnB0aW1lKGV4aWZfdGlkeSR0aW1lLCBmb3JtYXQgPSAiJUg6JU0lUyIpDQoNCmZpZyA8LSBwbG90X2x5KGV4aWZfdGlkeSwgeCA9IH50aW1lLCB0eXBlID0gJ2hpc3RvZ3JhbScpDQpmaWcNCmBgYA0KDQojIyBBbmFseXNpcw0KDQpGaW5hbCBkYXRhIHByZXAhDQoNClNsaW1taW5nIGRvd24gdGhlIEVYSUYgZGF0YSBmcmFtZS4uLi4NCg0KYGBge3J9DQoNCiNzbGltbWluZyBkb3duIGV4aWYgY29sdW1ucy4uLi4NCg0Kc2ltcGxlX2V4aWYgPC0gZXhpZl90aWR5ICU+JSBzZWxlY3QoYyhmbGlja3JfaWQsIGRhdGUsIG1vbnRoLCB0aW1lLCBzb2Z0d2FyZSwgamZpZnZlcnNpb24sIGJyaWdodG5lc3NfdmFsdWUpKQ0KYGBgDQoNCi4uLiBhbmQgYWRkaW5nIHRob3NlIEVYSUYgY29sdW1ucyB0byB0aGUgbWFpbiBkYXRhZnJhbWUNCg0KYGBge3J9DQoNCnNpbXBsZV9leGlmIDwtIHNpbXBsZV9leGlmICU+JQ0KICBtdXRhdGUoZmxpY2tyX2lkID0gYXMuY2hhcmFjdGVyKGZsaWNrcl9pZCkpDQoNCiMgYnJpbmdpbmcgaXQgYWxsIGZ1bGwgY2lyY2xlIHdpdGggdGhpcyB2YXJpYWJsZSBjaG9pY2UNCg0KYWxsX2ltZ19kYXRhIDwtIGltZ3NkX3JhdGlvICU+JSANCiAgbGVmdF9qb2luKHNpbXBsZV9leGlmLCBieSA9IGMoInVzaW5nX2lkIiA9ICJmbGlja3JfaWQiKSkNCg0KI3RoaXMgdGhyb3dzIGEgd2FybmluZyBhYm91dCBtYW55LXRvLW1hbnkgcmVsYXRpb25zaGlwcywgYnV0IHRoaXMgaXMgZXhwZWN0ZWQgYmVoYXZpb3INCmBgYA0KDQpUaGlzIHNwYWNlIHJlc2VydmVkIGZvciByZS1jcmVhdGluZyBzdWItZnJhbWVzIGZvciAnZmFjZXRpbmcnDQoNCmBgYHtyfQ0KDQoNCmBgYA0KDQpBdCBsYXN0LCBsZXQncyByZXZpc2l0IHRob3NlIGh5cG90aGVzZXMNCg0KIyMjIEgxIC0gSHVlIHZzIERhdGUNCg0KJEhvOiQgVGhlcmUgaXMgbm8gY29ycmVsYXRpb24gYmV0d2VlbiB0aW1lIG9mIHllYXIgYW5kIGNvbG9yIHZhbHVlcw0KDQokSGE6JCBXYXJtIGNvbG9yIHZhbHVlcyBhcmUgbW9yZSBwcm9taW5lbnQgYmV0d2VlbiBNYXkgYW5kIFNlcHRlbWJlcg0KDQpHdXQgQ2hlY2sgLSBEdWUgdG8gdGhlIHVuZXZlbiBkaXN0cmlidXRpb24gb2Ygc2FtcGxlIGltYWdlcyBvdmVyIHRpbWUsIEkgZG9uJ3QgaGF2ZSBhIHN0cm9uZyBleHBlY3RhdGlvbiBvZiB2YWxpZCByZXN1bHRzLg0KDQpgYGB7cn0NCg0KZXhpZl90aWR5JG1vbnRoIDwtIGFzLmludGVnZXIoZXhpZl90aWR5JG1vbnRoKQ0KDQpzZWFzb25fc3BsaXQgPC0gZXhpZl90aWR5ICU+JSBtdXRhdGUoDQogIHNlYXNvbiA9IGNhc2Vfd2hlbigNCiAgICBtb250aCA+PSA1ICYgbW9udGggPD0gOSB+IDEsIFRSVUUgfiAwKQ0KICApDQoNCnNlYXNvbl9jb3VudHMgPC0gdGFibGUoc2Vhc29uX3NwbGl0JHNlYXNvbikNCnNlYXNvbl9kYXRhIDwtIGRhdGEuZnJhbWUocmF0aW8gPSBuYW1lcyhzZWFzb25fY291bnRzKSwgY291bnQgPSBhcy52ZWN0b3Ioc2Vhc29uX2NvdW50cykpDQoNCnNlYXNvbl9kYXRhDQpgYGANCg0KT24gdGhlIG90aGVyIGhhbmQsIHN1bW1lciB2YWx1ZXMgKDYyMikgZG9uJ3Qgb3V0cmFnZW91c2x5IG91dG51bWJlciBub24tc3VtbWVyIHZhbHVlcy4NCg0KTm93IHRvIGNvbGxlY3QgdGhlIGh1ZSBpbmZvcm1hdGlvbiB3ZSB3aWxsIGJlIGNvbXBhcmluZw0KDQpgYGB7cn0NCg0KYWxsX2ltZ19kYXRhIDwtIGFsbF9pbWdfZGF0YSAlPiUgDQogIG11dGF0ZSh2aXNpYl93YXJtID0gdmlzaWJfcmVkX2NvdW50ICsgdmlzaWJfb3JhbmdlX2NvdW50ICsgdmlzaWJfeWVsbG93X2NvdW50ICsgdmlzaWJfbWFnX2NvdW50KSAlPiUNCiAgbXV0YXRlKHZpc2liX2Nvb2wgPSB2aXNpYl9ncmVlbl9jb3VudCArIHZpc2liX2N5YW5fY291bnQgKyB2aXNpYl9ibHVlX2NvdW50ICsgdmlzaWJfcHVycGxlX2NvdW50KQ0KICANCmgxX2ZyYW1lIDwtIGFsbF9pbWdfZGF0YSAlPiUgDQogIHNlbGVjdChjKHVzaW5nX2lkLCBzdWJfaW1nLCB0b3RhbF9waXhlbHMsIHZpc2liX3dhcm0sIHZpc2liX2Nvb2wsIGRhdGUsIG1vbnRoKSkgJT4lIA0KICBtdXRhdGUod2FybV9yYXRpbyA9IHZpc2liX3dhcm0gLyB0b3RhbF9waXhlbHMpICU+JSANCiAgbXV0YXRlKGNvb2xfcmF0aW8gPSB2aXNpYl9jb29sL3RvdGFsX3BpeGVscykgJT4lIA0KICBtdXRhdGUoc2Vhc29uID0gY2FzZV93aGVuKG1vbnRoID49IDUgJiBtb250aCA8PSA5IH4gMSwgVFJVRSB+IDApKSAlPiUNCiAgbXV0YXRlKHJhdGlvX2RpZmYgPSB3YXJtX3JhdGlvIC0gY29vbF9yYXRpbykNCmBgYA0KDQojIyMjIFRlc3RpbmcgVGltZQ0KDQpgYGB7cn0NCnQudGVzdCh3YXJtX3JhdGlvIH4gc2Vhc29uLCBoMV9mcmFtZSkNCmBgYA0KDQpEZXNwaXRlIG15IGhlc2l0YXRpb24sIHRoZSByZXN1bHRzIG9mIHRoZSBULXRlc3QgYXJlIHN0cm9uZ2x5IGluIGZhdm9yIG9mIHJlamVjdGluZyB0aGUgbnVsbCBoeXBvdGhlc2lzIHRoYXQgdGhlcmUgaXMgbm8gZGlmZmVyZW5jZSBpbiB2aXNpYmxlIHdhcm10aCB0aHJvdWdob3V0IHRoZSB5ZWFyLiBBIGhpZ2ggdC12YWx1ZSBhbmQgbG93IHAtdmFsdWUgYXJlIGJvdGggaW4gc3VwcG9ydCBvZiB0aGlzIGNvbmNsdXNpb24uDQoNCmBgYHtyfQ0KDQoNCmBgYA0KDQojIyMgSDIgLSBMaWdodG5lc3MgdnMgVGltZQ0KDQokSG86JCBUaGVyZSBpcyBubyBjb3JyZWxhdGlvbiBiZXR3ZWVuIHRpbWUgb2YgZGF5IGFuZCBsaWdodG5lc3MgdmFsdWVzDQoNCiRIYTokIExpZ2h0bmVzcyB2YWx1ZXMgYXJlIGhpZ2hlciBiZXR3ZWVuIDYgYW0gYW5kIDZwbQ0KDQpgYGB7cn0NCg0KaDJfZnJhbWUgPC0gYWxsX2ltZ19kYXRhICU+JSANCiAgc2VsZWN0KGModXNpbmdfaWQsIHN1Yl9pbWcsIHRpbWUsIGJyaWdodG5lc3NfdmFsdWUsIGxpZ2h0X21lYW5fdmFsKSklPiUgDQogIG11dGF0ZShkYXl0aW1lID0gY2FzZV93aGVuKHRpbWUgPj0gIjA2OjAwOjAwIiAmIHRpbWUgPD0gIjE4OjAwOjAwIiB+IDEsIFRSVUUgfiAwKSkNCg0KYGBgDQoNCiMjIyBIMyAtIFNhdHVyYXRpb24gdnMgU3ViamVjdA0KDQokSG86JCBUaGVyZSBpcyBubyBjb3JyZWxhdGlvbiBiZXR3ZWVuIHNhdHVyYXRpb24gYW5kIGJlaW5nIGEgcGljdHVyZSBvZiBteSBjYXQNCg0KJEhhOiQgTG93IHNhdHVyYXRpb24gdmFsdWVzIGFyZSBpbmNyZWFzaW5nbHkgY29tbW9uIG92ZXIgdGltZSwgZXNwZWNpYWxseSBpbiBjZW50cmFsIHN1Yi1pbWFnZXMNCg0KIyMjIEg0IC0gVml2aWRuZXNzIHZzIEltYWdlIFR5cGUNCg0KJEhvOiQgVml2aWQgcmF0aW8gKHBlcmNlbnRhZ2Ugb2Ygdml2aWQgcGl4ZWxzKSBpcyB1bmlmb3JtbHkgZGlzdHJpYnV0ZWQgYW1vbmcgYWxsIFNvZnR3YXJlIHR5cGVzDQoNCiRIYTokIFZpdmlkIHJhdGlvIGlzIGNvbnNpc3RlbnRseSBoaWdoZXN0IGluIFNsb3cgU2h1dHRlciBDYW0gcGhvdG9zIHdpdGhvdXQgSkZJRiB2YWx1ZXMNCg0KIyBDb25jbHVzaW9uDQo=